Skip to content

Commit ab42add

Browse files
authored
fix: let props_to_labels take property from log line (#11)
1 parent cbdb987 commit ab42add

File tree

3 files changed

+29
-13
lines changed

3 files changed

+29
-13
lines changed

logging_loki/emitter.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ class LokiEmitter:
2020
"""Base Loki emitter class."""
2121

2222
success_response_code = const.success_response_code
23-
level_tag = const.level_tag
24-
logger_tag = const.logger_tag
2523
label_allowed_chars = const.label_allowed_chars
2624
label_replace_with = const.label_replace_with
2725
session_class = requests.Session
@@ -32,7 +30,9 @@ def __init__(self,
3230
headers: Optional[dict] = None,
3331
auth: BasicAuth = None,
3432
as_json: bool = False,
35-
props_to_labels: Optional[list[str]] = None
33+
props_to_labels: Optional[list[str]] = None,
34+
level_tag: Optional[str] = const.level_tag,
35+
logger_tag: Optional[str] = const.logger_tag
3636
):
3737
"""
3838
Create new Loki emitter.
@@ -53,8 +53,12 @@ def __init__(self,
5353
self.auth = auth
5454
#: Optional bool, send record as json?
5555
self.as_json = as_json
56-
#: Optional list, send record as json?
56+
#: Optional list, convert properties to loki labels
5757
self.props_to_labels = props_to_labels or []
58+
#: Label name indicating logging level.
59+
self.level_tag: str = level_tag
60+
#: Label name indicating logger name.
61+
self.logger_tag: str = logger_tag
5862

5963
self._session: Optional[requests.Session] = None
6064
self._lock = threading.Lock()
@@ -102,14 +106,21 @@ def format_label(self, label: str) -> str:
102106
label = label.replace(char_from, char_to)
103107
return "".join(char for char in label if char in self.label_allowed_chars)
104108

105-
def build_tags(self, record: logging.LogRecord) -> Dict[str, Any]:
109+
def build_tags(self, record: logging.LogRecord, line: str) -> Dict[str, Any]:
106110
"""Return tags that must be send to Loki with a log record."""
107111
tags = dict(self.tags) if isinstance(self.tags, ConvertingDict) else self.tags
108112
tags = copy.deepcopy(tags)
109-
tags[self.level_tag] = record.levelname.lower()
110-
tags[self.logger_tag] = record.name
111-
112-
extra_tags = {k: getattr(record, k) for k in self.props_to_labels if getattr(record, k, None)}
113+
if self.level_tag:
114+
tags[self.level_tag] = record.levelname.lower()
115+
if self.logger_tag:
116+
tags[self.logger_tag] = record.name
117+
118+
extra_tags = {}
119+
if self.props_to_labels:
120+
jsonline = json.loads(line)
121+
for k in self.props_to_labels:
122+
if prop_value := getattr(record, k, None) or jsonline.get(k, None):
123+
extra_tags.update({k: prop_value})
113124
if isinstance(passed_tags := getattr(record, "tags", {}), dict):
114125
extra_tags = extra_tags | passed_tags
115126

@@ -121,9 +132,9 @@ def build_tags(self, record: logging.LogRecord) -> Dict[str, Any]:
121132

122133
return tags
123134

124-
def build_payload(self, record: logging.LogRecord, line) -> dict:
135+
def build_payload(self, record: logging.LogRecord, line: str) -> dict:
125136
"""Build JSON payload with a log entry."""
126-
labels = self.build_tags(record)
137+
labels = self.build_tags(record, line)
127138
ns = 1e9
128139
ts = str(int(time.time() * ns))
129140

logging_loki/handlers.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from queue import Queue
88
import time
99
from typing import Optional, Union
10+
from logging_loki import const
1011

1112
from logging_loki.emitter import BasicAuth, LokiEmitter
1213

@@ -51,6 +52,8 @@ def __init__(
5152
auth: Optional[BasicAuth] = None,
5253
as_json: Optional[bool] = False,
5354
props_to_labels: Optional[list[str]] = None,
55+
level_tag: Optional[str] = const.level_tag,
56+
logger_tag: Optional[str] = const.logger_tag
5457
):
5558
"""
5659
Create new Loki logging handler.
@@ -62,10 +65,12 @@ def __init__(
6265
headers: Optional record with headers that are send with each POST to loki.
6366
as_json: Flag to support sending entire JSON record instead of only the message.
6467
props_to_labels: List of properties that should be converted to loki labels.
68+
level_tag: Label name indicating logging level.
69+
logger_tag: Label name indicating logger name.
6570
6671
"""
6772
super().__init__()
68-
self.emitter = LokiEmitter(url, tags, headers, auth, as_json, props_to_labels)
73+
self.emitter = LokiEmitter(url, tags, headers, auth, as_json, props_to_labels, level_tag, logger_tag)
6974

7075
def handleError(self, record): # noqa: N802
7176
"""Close emitter and let default handler take actions on error."""

tests/test_emitter_v1.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def test_can_build_tags_from_converting_dict(emitter_v1):
174174

175175
logger = logging.getLogger(logger_name)
176176
emitter: LokiEmitter = logger.handlers[0].handler.emitter
177-
emitter.build_tags(create_record())
177+
emitter.build_tags(create_record(), '{}')
178178

179179
def test_batch_records_sent_to_emitter_url(emitter_v1):
180180
emitter, session = emitter_v1

0 commit comments

Comments
 (0)