stdlogkit 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stdlogkit
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A small stdlib logging auto-configuration kit with colored multiline logs and log-once helpers.
5
5
  Author-email: Your Name <you@example.com>
6
6
  License-Expression: Apache-2.0
@@ -27,16 +27,17 @@ Requires-Dist: twine>=5; extra == "dev"
27
27
 
28
28
  # stdlogkit
29
29
 
30
- `stdlogkit` is a tiny, vLLM-independent wrapper around Python's standard
31
- `logging` package.
30
+ `stdlogkit` is a vLLM-style, vLLM-independent wrapper around Python's
31
+ standard `logging` package.
32
32
 
33
- Importing it configures standard logging immediately:
33
+ Importing it configures logging immediately. Like vLLM, the default
34
+ configuration installs a handler on a named root logger, `stdlogkit`, and child
35
+ loggers propagate to it.
34
36
 
35
37
  ```python
36
- import logging
37
- import stdlogkit # noqa: F401
38
+ import stdlogkit
38
39
 
39
- logger = logging.getLogger(__name__)
40
+ logger = stdlogkit.init_logger("stdlogkit.app")
40
41
  logger.info("hello")
41
42
  logger.warning_once("this warning appears once")
42
43
  ```
@@ -48,33 +49,48 @@ INFO 06-24 12:00:01 [example.py:5] hello
48
49
  WARNING 06-24 12:00:01 [example.py:6] this warning appears once
49
50
  ```
50
51
 
51
- ## Features
52
+ The default format intentionally does not include process name or PID, matching
53
+ vLLM. Single-process and multiprocessing logs therefore use the same fields.
52
54
 
53
- - Uses only Python's standard `logging` machinery.
54
- - Auto-configures logging on `import stdlogkit`.
55
- - Configures the root logger by default, so normal `logging.getLogger(...)`
56
- calls work immediately.
57
- - Adds `debug_once`, `info_once`, and `warning_once` to standard logger
58
- instances.
59
- - Supports multiline log alignment.
60
- - Supports ANSI colors with `auto`, forced-on, and forced-off modes.
61
- - Supports custom JSON config through `logging.config.dictConfig`.
62
- - Includes optional uvicorn access-log filtering helpers.
63
- - Has no runtime dependency on vLLM or any other third-party package.
55
+ ## vLLM Compatibility
56
+
57
+ This package mirrors vLLM's logging behavior while remaining fully decoupled:
58
+
59
+ - Uses `logging.config.dictConfig`.
60
+ - Configures one named logger by default, `stdlogkit`, with `propagate=false`.
61
+ - Child loggers such as `stdlogkit.app` have no handler and propagate upward.
62
+ - `init_logger(name)` patches `debug_once`, `info_once`, and `warning_once`
63
+ onto that returned logger instance.
64
+ - `*_once` uses `functools.lru_cache`, so arguments must be hashable, as in
65
+ vLLM.
66
+ - Multiline logs are aligned with the same prefix strategy as vLLM.
67
+ - Color constants match vLLM exactly.
68
+
69
+ ## Colors
70
+
71
+ The ANSI color values are identical to vLLM:
72
+
73
+ | Level/field | ANSI code |
74
+ | --- | --- |
75
+ | `DEBUG` | `\033[37m` |
76
+ | `INFO` | `\033[32m` |
77
+ | `WARNING` | `\033[33m` |
78
+ | `ERROR` | `\033[31m` |
79
+ | `CRITICAL` | `\033[35m` |
80
+ | timestamp and file info | `\033[90m` |
81
+ | reset | `\033[0m` |
64
82
 
65
83
  ## Environment Variables
66
84
 
67
85
  | Variable | Default | Description |
68
86
  | --- | --- | --- |
69
87
  | `STDLOGKIT_CONFIGURE_LOGGING` | `1` | Set `0` to disable auto configuration. |
70
- | `STDLOGKIT_LOGGING_LEVEL` | `INFO` | Root or named logger level. |
88
+ | `STDLOGKIT_LOGGING_LEVEL` | `INFO` | Named logger and handler level. |
71
89
  | `STDLOGKIT_LOGGING_STREAM` | `ext://sys.stdout` | Handler stream. |
72
90
  | `STDLOGKIT_LOGGING_PREFIX` | empty | Prefix prepended to every log line. |
73
91
  | `STDLOGKIT_LOGGING_COLOR` | `auto` | `auto`, `1`, or `0`. |
74
92
  | `STDLOGKIT_LOGGING_CONFIG_PATH` | unset | Path to a JSON `dictConfig` file. |
75
- | `STDLOGKIT_LOGGER_NAME` | empty | Configure a named logger instead of root. |
76
- | `STDLOGKIT_ROOT_DIR` | current working directory | Base path for relative file display. |
77
- | `STDLOGKIT_SHOW_REL_PATH` | `debug` | `debug`, `always`, or `never`. |
93
+ | `STDLOGKIT_LOGGER_NAME` | `stdlogkit` | Named logger to configure. |
78
94
  | `NO_COLOR` | `0` | Standard flag to disable ANSI colors. |
79
95
 
80
96
  ## Custom JSON Config
@@ -98,9 +114,12 @@ WARNING 06-24 12:00:01 [example.py:6] this warning appears once
98
114
  "stream": "ext://sys.stdout"
99
115
  }
100
116
  },
101
- "root": {
102
- "handlers": ["console"],
103
- "level": "INFO"
117
+ "loggers": {
118
+ "stdlogkit": {
119
+ "handlers": ["console"],
120
+ "level": "INFO",
121
+ "propagate": false
122
+ }
104
123
  }
105
124
  }
106
125
  ```
@@ -111,32 +130,13 @@ Run with:
111
130
  STDLOGKIT_LOGGING_CONFIG_PATH=/path/to/logging.json python app.py
112
131
  ```
113
132
 
114
- ## Named Logger Mode
115
-
116
- By default, stdlogkit configures the root logger. If you want behavior closer
117
- to a framework-specific logger, configure only a named logger:
118
-
119
- ```bash
120
- STDLOGKIT_LOGGER_NAME=my_app python app.py
121
- ```
122
-
123
- Then loggers under `my_app.*` propagate to that logger:
124
-
125
- ```python
126
- import logging
127
- import stdlogkit # noqa: F401
128
-
129
- logger = logging.getLogger("my_app.service")
130
- logger.info("hello")
131
- ```
132
-
133
133
  ## Development
134
134
 
135
135
  ```bash
136
136
  cd standalone_logging_pkg
137
137
  uv venv --python 3.12
138
138
  uv pip install -e ".[dev]"
139
- .venv/bin/python -m pytest -q
139
+ PYTHONPATH=src .venv/bin/python -m unittest discover -s tests -v
140
140
  uv build
141
141
  ```
142
142
 
@@ -1,15 +1,16 @@
1
1
  # stdlogkit
2
2
 
3
- `stdlogkit` is a tiny, vLLM-independent wrapper around Python's standard
4
- `logging` package.
3
+ `stdlogkit` is a vLLM-style, vLLM-independent wrapper around Python's
4
+ standard `logging` package.
5
5
 
6
- Importing it configures standard logging immediately:
6
+ Importing it configures logging immediately. Like vLLM, the default
7
+ configuration installs a handler on a named root logger, `stdlogkit`, and child
8
+ loggers propagate to it.
7
9
 
8
10
  ```python
9
- import logging
10
- import stdlogkit # noqa: F401
11
+ import stdlogkit
11
12
 
12
- logger = logging.getLogger(__name__)
13
+ logger = stdlogkit.init_logger("stdlogkit.app")
13
14
  logger.info("hello")
14
15
  logger.warning_once("this warning appears once")
15
16
  ```
@@ -21,33 +22,48 @@ INFO 06-24 12:00:01 [example.py:5] hello
21
22
  WARNING 06-24 12:00:01 [example.py:6] this warning appears once
22
23
  ```
23
24
 
24
- ## Features
25
+ The default format intentionally does not include process name or PID, matching
26
+ vLLM. Single-process and multiprocessing logs therefore use the same fields.
25
27
 
26
- - Uses only Python's standard `logging` machinery.
27
- - Auto-configures logging on `import stdlogkit`.
28
- - Configures the root logger by default, so normal `logging.getLogger(...)`
29
- calls work immediately.
30
- - Adds `debug_once`, `info_once`, and `warning_once` to standard logger
31
- instances.
32
- - Supports multiline log alignment.
33
- - Supports ANSI colors with `auto`, forced-on, and forced-off modes.
34
- - Supports custom JSON config through `logging.config.dictConfig`.
35
- - Includes optional uvicorn access-log filtering helpers.
36
- - Has no runtime dependency on vLLM or any other third-party package.
28
+ ## vLLM Compatibility
29
+
30
+ This package mirrors vLLM's logging behavior while remaining fully decoupled:
31
+
32
+ - Uses `logging.config.dictConfig`.
33
+ - Configures one named logger by default, `stdlogkit`, with `propagate=false`.
34
+ - Child loggers such as `stdlogkit.app` have no handler and propagate upward.
35
+ - `init_logger(name)` patches `debug_once`, `info_once`, and `warning_once`
36
+ onto that returned logger instance.
37
+ - `*_once` uses `functools.lru_cache`, so arguments must be hashable, as in
38
+ vLLM.
39
+ - Multiline logs are aligned with the same prefix strategy as vLLM.
40
+ - Color constants match vLLM exactly.
41
+
42
+ ## Colors
43
+
44
+ The ANSI color values are identical to vLLM:
45
+
46
+ | Level/field | ANSI code |
47
+ | --- | --- |
48
+ | `DEBUG` | `\033[37m` |
49
+ | `INFO` | `\033[32m` |
50
+ | `WARNING` | `\033[33m` |
51
+ | `ERROR` | `\033[31m` |
52
+ | `CRITICAL` | `\033[35m` |
53
+ | timestamp and file info | `\033[90m` |
54
+ | reset | `\033[0m` |
37
55
 
38
56
  ## Environment Variables
39
57
 
40
58
  | Variable | Default | Description |
41
59
  | --- | --- | --- |
42
60
  | `STDLOGKIT_CONFIGURE_LOGGING` | `1` | Set `0` to disable auto configuration. |
43
- | `STDLOGKIT_LOGGING_LEVEL` | `INFO` | Root or named logger level. |
61
+ | `STDLOGKIT_LOGGING_LEVEL` | `INFO` | Named logger and handler level. |
44
62
  | `STDLOGKIT_LOGGING_STREAM` | `ext://sys.stdout` | Handler stream. |
45
63
  | `STDLOGKIT_LOGGING_PREFIX` | empty | Prefix prepended to every log line. |
46
64
  | `STDLOGKIT_LOGGING_COLOR` | `auto` | `auto`, `1`, or `0`. |
47
65
  | `STDLOGKIT_LOGGING_CONFIG_PATH` | unset | Path to a JSON `dictConfig` file. |
48
- | `STDLOGKIT_LOGGER_NAME` | empty | Configure a named logger instead of root. |
49
- | `STDLOGKIT_ROOT_DIR` | current working directory | Base path for relative file display. |
50
- | `STDLOGKIT_SHOW_REL_PATH` | `debug` | `debug`, `always`, or `never`. |
66
+ | `STDLOGKIT_LOGGER_NAME` | `stdlogkit` | Named logger to configure. |
51
67
  | `NO_COLOR` | `0` | Standard flag to disable ANSI colors. |
52
68
 
53
69
  ## Custom JSON Config
@@ -71,9 +87,12 @@ WARNING 06-24 12:00:01 [example.py:6] this warning appears once
71
87
  "stream": "ext://sys.stdout"
72
88
  }
73
89
  },
74
- "root": {
75
- "handlers": ["console"],
76
- "level": "INFO"
90
+ "loggers": {
91
+ "stdlogkit": {
92
+ "handlers": ["console"],
93
+ "level": "INFO",
94
+ "propagate": false
95
+ }
77
96
  }
78
97
  }
79
98
  ```
@@ -84,32 +103,13 @@ Run with:
84
103
  STDLOGKIT_LOGGING_CONFIG_PATH=/path/to/logging.json python app.py
85
104
  ```
86
105
 
87
- ## Named Logger Mode
88
-
89
- By default, stdlogkit configures the root logger. If you want behavior closer
90
- to a framework-specific logger, configure only a named logger:
91
-
92
- ```bash
93
- STDLOGKIT_LOGGER_NAME=my_app python app.py
94
- ```
95
-
96
- Then loggers under `my_app.*` propagate to that logger:
97
-
98
- ```python
99
- import logging
100
- import stdlogkit # noqa: F401
101
-
102
- logger = logging.getLogger("my_app.service")
103
- logger.info("hello")
104
- ```
105
-
106
106
  ## Development
107
107
 
108
108
  ```bash
109
109
  cd standalone_logging_pkg
110
110
  uv venv --python 3.12
111
111
  uv pip install -e ".[dev]"
112
- .venv/bin/python -m pytest -q
112
+ PYTHONPATH=src .venv/bin/python -m unittest discover -s tests -v
113
113
  uv build
114
114
  ```
115
115
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "stdlogkit"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "A small stdlib logging auto-configuration kit with colored multiline logs and log-once helpers."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -22,7 +22,7 @@ from stdlogkit.logger import (
22
22
  )
23
23
  from stdlogkit.timing import logtime
24
24
 
25
- __version__ = "0.1.0"
25
+ __version__ = "0.2.0"
26
26
 
27
27
  configure_logging()
28
28
 
@@ -23,9 +23,7 @@ class Settings:
23
23
  logging_stream: str = "ext://sys.stdout"
24
24
  logging_config_path: str | None = None
25
25
  logging_color: str = "auto"
26
- logger_name: str = ""
27
- root_dir: str | None = None
28
- show_rel_path: str = "debug"
26
+ logger_name: str = "stdlogkit"
29
27
  no_color: bool = False
30
28
 
31
29
  @classmethod
@@ -38,9 +36,7 @@ class Settings:
38
36
  logging_prefix=os.getenv("STDLOGKIT_LOGGING_PREFIX", ""),
39
37
  logging_stream=os.getenv("STDLOGKIT_LOGGING_STREAM", "ext://sys.stdout"),
40
38
  logging_config_path=os.getenv("STDLOGKIT_LOGGING_CONFIG_PATH"),
41
- logging_color=os.getenv("STDLOGKIT_LOGGING_COLOR", "auto").lower(),
42
- logger_name=os.getenv("STDLOGKIT_LOGGER_NAME", ""),
43
- root_dir=os.getenv("STDLOGKIT_ROOT_DIR"),
44
- show_rel_path=os.getenv("STDLOGKIT_SHOW_REL_PATH", "debug").lower(),
39
+ logging_color=os.getenv("STDLOGKIT_LOGGING_COLOR", "auto"),
40
+ logger_name=os.getenv("STDLOGKIT_LOGGER_NAME", "stdlogkit"),
45
41
  no_color=_bool_from_env("NO_COLOR", False),
46
42
  )
@@ -0,0 +1,90 @@
1
+ """Formatters used by stdlogkit."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from pathlib import Path
7
+
8
+ from stdlogkit.env import Settings
9
+
10
+
11
+ class NewLineFormatter(logging.Formatter):
12
+ """Adds logging prefix to newlines to align multi-line messages."""
13
+
14
+ def __init__(self, fmt, datefmt=None, style="%"):
15
+ super().__init__(fmt, datefmt, style)
16
+
17
+ self.use_relpath = Settings.from_env().logging_level == "DEBUG"
18
+ if self.use_relpath:
19
+ self.root_dir = Path(__file__).resolve().parent.parent.parent
20
+
21
+ def format(self, record):
22
+ def shrink_path(relpath: Path) -> str:
23
+ parts = list(relpath.parts)
24
+ new_parts = []
25
+ if parts and parts[0] == "stdlogkit":
26
+ parts = parts[1:]
27
+ if parts and parts[0] == "v1":
28
+ new_parts += parts[:2]
29
+ parts = parts[2:]
30
+ elif parts:
31
+ new_parts += parts[:1]
32
+ parts = parts[1:]
33
+ if len(parts) > 2:
34
+ new_parts += ["..."] + parts[-2:]
35
+ else:
36
+ new_parts += parts
37
+ return "/".join(new_parts)
38
+
39
+ if self.use_relpath:
40
+ abs_path = getattr(record, "pathname", None)
41
+ if abs_path:
42
+ try:
43
+ relpath = Path(abs_path).resolve().relative_to(self.root_dir)
44
+ except Exception:
45
+ relpath = Path(record.filename)
46
+ else:
47
+ relpath = Path(record.filename)
48
+ record.fileinfo = shrink_path(relpath)
49
+ else:
50
+ record.fileinfo = record.filename
51
+
52
+ msg = super().format(record)
53
+ if record.message != "":
54
+ parts = msg.split(record.message)
55
+ msg = msg.replace("\n", "\r\n" + parts[0])
56
+ return msg
57
+
58
+
59
+ class ColoredFormatter(NewLineFormatter):
60
+ """Adds ANSI color codes to log levels for terminal output."""
61
+
62
+ COLORS = {
63
+ "DEBUG": "\033[37m",
64
+ "INFO": "\033[32m",
65
+ "WARNING": "\033[33m",
66
+ "ERROR": "\033[31m",
67
+ "CRITICAL": "\033[35m",
68
+ }
69
+ GREY = "\033[90m"
70
+ RESET = "\033[0m"
71
+
72
+ def __init__(self, fmt, datefmt=None, style="%"):
73
+ if fmt:
74
+ fmt = fmt.replace("%(asctime)s", f"{self.GREY}%(asctime)s{self.RESET}")
75
+ fmt = fmt.replace(
76
+ "[%(fileinfo)s:%(lineno)d]",
77
+ f"{self.GREY}[%(fileinfo)s:%(lineno)d]{self.RESET}",
78
+ )
79
+ super().__init__(fmt, datefmt, style)
80
+
81
+ def format(self, record):
82
+ orig_levelname = record.levelname
83
+ if (color_code := self.COLORS.get(record.levelname)) is not None:
84
+ record.levelname = f"{color_code}{record.levelname}{self.RESET}"
85
+
86
+ msg = super().format(record)
87
+
88
+ record.levelname = orig_levelname
89
+
90
+ return msg