Skip to content

Commit 728f019

Browse files
committed
formatted message for troubleshooting
1 parent 86c32c1 commit 728f019

File tree

2 files changed

+73
-32
lines changed

2 files changed

+73
-32
lines changed

packages/service-library/src/servicelib/logging_utils.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
from pathlib import Path
1717
from typing import Any, TypeAlias, TypedDict, TypeVar
1818

19+
from models_library.utils.json_serialization import json_dumps
20+
21+
from .error_codes import ErrorCodeStr
1922
from .utils_secrets import mask_sensitive_data
2023

2124
_logger = logging.getLogger(__name__)
@@ -320,24 +323,62 @@ def log_catch(logger: logging.Logger, *, reraise: bool = True) -> Iterator[None]
320323

321324
class LogExtra(TypedDict, total=False):
322325
log_uid: str
326+
log_oec: str
323327

324328

325329
LogLevelInt: TypeAlias = int
326330
LogMessageStr: TypeAlias = str
327331

328332

329-
def get_log_record_extra(*, user_id: int | str | None = None) -> LogExtra | None:
333+
def get_log_record_extra(
334+
*,
335+
user_id: int | str | None = None,
336+
error_code: str | None = None,
337+
) -> LogExtra | None:
330338
extra: LogExtra = {}
339+
331340
if user_id:
332341
assert int(user_id) > 0 # nosec
333342
extra["log_uid"] = f"{user_id}"
343+
if error_code:
344+
extra["log_oec"] = error_code
345+
334346
return extra or None
335347

336348

337349
def _un_capitalize(s: str) -> str:
338350
return s[:1].lower() + s[1:] if s else ""
339351

340352

353+
def create_troubleshotting_log_message(
354+
message_to_user: str,
355+
error: BaseException,
356+
error_code: ErrorCodeStr,
357+
error_context: dict[str, Any] | None = None,
358+
tip: str | None = None,
359+
) -> str:
360+
"""Create a formatted message for _logger.exception(...)
361+
362+
Arguments:
363+
message_to_user -- A user-friendly message to be displayed on the front-end explaining the issue in simple terms.
364+
error -- the instance of the handled exception
365+
error_code -- A unique error code (e.g., OEC or osparc-specific) to identify the type or source of the error for easier tracking.
366+
error_context -- Additional context surrounding the exception, such as environment variables or function-specific data. This can be derived from exc.error_context() (relevant when using the OsparcErrorMixin)
367+
tip -- Helpful suggestions or possible solutions explaining why the error may have occurred and how it could potentially be resolved
368+
"""
369+
debug_data = json_dumps(
370+
{
371+
"exception_details": f"{error}",
372+
"error_code": error_code,
373+
"context": error_context,
374+
"tip": tip,
375+
},
376+
indent=1,
377+
)
378+
379+
return f"{message_to_user}.\n{debug_data}"
380+
381+
341382
@contextmanager
342383
def log_context(
343384
logger: logging.Logger,

packages/service-library/tests/test_error_codes.py

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,50 @@
55

66
import logging
77

8+
import pytest
89
from servicelib.error_codes import create_error_code, parse_error_code
910

1011
logger = logging.getLogger(__name__)
1112

1213

13-
def test_error_code_use_case(caplog):
14+
def test_error_code_use_case(caplog: pytest.LogCaptureFixture):
1415
"""use case for error-codes"""
15-
try:
16+
with pytest.raises(RuntimeError) as exc_info:
1617
raise RuntimeError("Something unexpected went wrong")
17-
except Exception as err:
18-
# 1. Unexpected ERROR
1918

20-
# 2. create error-code
21-
error_code = create_error_code(err)
19+
# 1. Unexpected ERROR
20+
err = exc_info.value
2221

23-
# 3. log all details in service
24-
caplog.clear()
22+
# 2. create error-code
23+
error_code = create_error_code(err)
2524

26-
# Can add a formatter that prefix error-codes
27-
syslog = logging.StreamHandler()
28-
syslog.setFormatter(
29-
logging.Formatter("%(asctime)s %(error_code)s : %(message)s")
30-
)
31-
logger.addHandler(syslog)
25+
# 3. log all details in service
26+
caplog.clear()
3227

33-
logger.error("Fake Unexpected error", extra={"error_code": error_code})
28+
# Can add a formatter that prefix error-codes
29+
syslog = logging.StreamHandler()
30+
syslog.setFormatter(logging.Formatter("%(asctime)s %(error_code)s : %(message)s"))
31+
logger.addHandler(syslog)
3432

35-
# logs something like E.g. 2022-07-06 14:31:13,432 OEC:140350117529856 : Fake Unexpected error
36-
assert parse_error_code(
37-
f"2022-07-06 14:31:13,432 {error_code} : Fake Unexpected error"
38-
) == {
39-
error_code,
40-
}
33+
logger.exception("Fake Unexpected error", extra={"error_code": error_code})
4134

42-
assert caplog.records[0].error_code == error_code
43-
assert caplog.records[0]
35+
# logs something like E.g. 2022-07-06 14:31:13,432 OEC:140350117529856 : Fake Unexpected error
36+
assert parse_error_code(
37+
f"2022-07-06 14:31:13,432 {error_code} : Fake Unexpected error"
38+
) == {
39+
error_code,
40+
}
4441

45-
logger.error("Fake without error_code")
42+
assert caplog.records[0].error_code == error_code
43+
assert caplog.records[0]
4644

47-
# 4. inform user (e.g. with new error or sending message)
48-
user_message = (
49-
f"This is a user-friendly message to inform about an error. [{error_code}]"
50-
)
45+
logger.exception("Fake without error_code")
5146

52-
assert parse_error_code(user_message) == {
53-
error_code,
54-
}
47+
# 4. inform user (e.g. with new error or sending message)
48+
user_message = (
49+
f"This is a user-friendly message to inform about an error. [{error_code}]"
50+
)
51+
52+
assert parse_error_code(user_message) == {
53+
error_code,
54+
}

0 commit comments

Comments
 (0)