Skip to content

Commit 6351294

Browse files
committed
🎨 Use dedicated socket for telemetry logging
can't use syslog as systemd/journal intercepts
1 parent 58cc087 commit 6351294

3 files changed

Lines changed: 46 additions & 25 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ All notable changes to this library are documented in this file.
1111
- Disable IEMAccess write of wind information from the DSM due to inprecision
1212
of reported units in MPH.
1313
- Return signature of `str2multipolygon` changed to include a list of errors.
14-
- `webutil.write_telemetry` now writes to syslog (local1.info) with
14+
- `webutil.write_telemetry` now writes to dedicated socket
15+
`/run/rsyslog/telemetry.sock` with
1516
`webutil.TELEMETRY_PREFIX` and a JSON payload string.
1617

1718
### New Features

src/pyiem/webutil.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import os
88
import random
99
import re
10+
import socket
1011
import string
1112
import sys
12-
import syslog
1313
import traceback
1414
import warnings
1515
from collections import namedtuple
@@ -77,6 +77,7 @@
7777
],
7878
)
7979
TELEMETRY_PREFIX = "Telemetry "
80+
TELEMETRY_SOCKET = "/run/rsyslog/telemetry.sock"
8081
XSS_SENTINEL = "XSS"
8182
MEMCACHED_HIT = "_mhit"
8283

@@ -205,19 +206,21 @@ def model_to_rst(model: BaseModel) -> str:
205206

206207

207208
def write_telemetry(data: TELEMETRY) -> bool:
208-
"""Write telemetry to syslog."""
209+
"""Best-effort telemetry write."""
210+
# 141 is local1.notice and is critical to make this work.
211+
# The TELEMETRY_PREFIX becomes the syslog tag
212+
payload = (
213+
"<141>"
214+
+ TELEMETRY_PREFIX
215+
+ json.dumps(data._asdict(), separators=(",", ":"), sort_keys=True)
216+
).encode("utf-8")
217+
# We need to avoid syslog as systemd/journal will intercept this and
218+
# fill logs quickly.
209219
try:
210-
syslog.syslog(
211-
syslog.LOG_LOCAL1 | syslog.LOG_INFO,
212-
TELEMETRY_PREFIX
213-
+ json.dumps(
214-
data._asdict(),
215-
separators=(",", ":"),
216-
sort_keys=True,
217-
),
218-
)
219-
except Exception as exp:
220-
LOG.info("write_telemetry failed: %s", exp, exc_info=True)
220+
with socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) as sock:
221+
sock.setblocking(False)
222+
sock.sendto(payload, TELEMETRY_SOCKET)
223+
except (BlockingIOError, OSError):
221224
return False
222225
return True
223226

tests/test_webutil.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from pyiem.reference import ISO8601
2121
from pyiem.webutil import (
2222
TELEMETRY,
23+
TELEMETRY_SOCKET,
2324
CGIModel,
2425
ListOrCSVType,
2526
_is_xss_payload,
@@ -552,23 +553,39 @@ def test_disable_parse_times():
552553

553554

554555
def test_add_telemetry():
555-
"""Test adding something to the queue."""
556-
assert write_telemetry(
557-
TELEMETRY(
558-
timing=1,
559-
status_code=200,
560-
client_addr=None,
561-
app="test",
562-
request_uri="",
563-
vhost="",
564-
valid=datetime.now().strftime(ISO8601),
556+
"""Test adding something to the telemetry socket."""
557+
data = TELEMETRY(
558+
timing=1,
559+
status_code=200,
560+
client_addr=None,
561+
app="test",
562+
request_uri="",
563+
vhost="",
564+
valid=datetime.now().strftime(ISO8601),
565+
)
566+
socket_mock = mock.MagicMock()
567+
cm_mock = mock.MagicMock()
568+
cm_mock.__enter__.return_value = socket_mock
569+
cm_mock.__exit__.return_value = False
570+
with mock.patch("pyiem.webutil.socket.socket", return_value=cm_mock):
571+
assert write_telemetry(data)
572+
573+
socket_mock.setblocking.assert_called_once_with(False)
574+
socket_mock.sendto.assert_called_once_with(
575+
b"<141>Telemetry "
576+
+ (
577+
b'{"app":"test","client_addr":null,"request_uri":"",'
578+
b'"status_code":200,"timing":1,"valid":"'
579+
+ data.valid.encode("utf-8")
580+
+ b'","vhost":""}'
565581
),
582+
TELEMETRY_SOCKET,
566583
)
567584

568585

569586
def test_add_telemetry_failure_is_swallowed():
570587
"""Test telemetry failures stay contained inside write_telemetry."""
571-
with mock.patch("pyiem.webutil.syslog.syslog", side_effect=RuntimeError()):
588+
with mock.patch("pyiem.webutil.socket.socket", side_effect=OSError()):
572589
assert not write_telemetry(
573590
TELEMETRY(
574591
timing=1,

0 commit comments

Comments
 (0)