Skip to content

Commit 38eed47

Browse files
authored
Merge pull request #6 from zcsadmin/feat/add_request_state
feat: add request state
2 parents ec0be53 + 538fc36 commit 38eed47

File tree

7 files changed

+103
-10
lines changed

7 files changed

+103
-10
lines changed

.github/workflows/publish-tag.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ jobs:
2828
service_account: github-actions-sa@ai-accounting-405809.iam.gserviceaccount.com
2929
access_token_lifetime: 600s
3030

31+
- name: Set application version
32+
run: |
33+
sed -i 's|version = "\(.*\)"|version = "${{ github.ref_name }}"|g' app/pyproject.toml &&
34+
cat app/pyproject.toml
35+
3136
- name: Setup Python
3237
uses: actions/setup-python@v3
3338

app/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "zcs-python-core"
7-
version = "0.3.0"
7+
version = "0.dev0"
88
description = "A Python library that provides core functionalities for ZCS projects"
99

1010
[tool.setuptools.packages.find]

app/src/zcs/core/exception/exception.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import random
22
import string
33
import traceback
4+
from typing import Optional
5+
46

57
class ZcsException(Exception):
68

7-
def __init__(self, user_message = "Unknown error.", error_source = "Unspecified", status_code = 500, internal_message = None):
9+
def __init__(
10+
self, user_message: str = "Unknown error.", error_source: str = "Unspecified",
11+
status_code: int = 500, internal_message: Optional[str] = None):
812
super().__init__(user_message)
9-
13+
1014
self.__error_source = error_source
1115
self.__status_code = status_code
1216
self.__user_message = user_message
@@ -15,16 +19,16 @@ def __init__(self, user_message = "Unknown error.", error_source = "Unspecified"
1519

1620
def get_status_code(self):
1721
return self.__status_code
18-
22+
1923
def get_error_source(self):
2024
return self.__error_source
21-
25+
2226
def get_user_message(self):
2327
return self.__user_message
24-
28+
2529
def get_internal_message(self):
2630
return self.__internal_message
27-
31+
2832
def get_error_code(self):
2933
return self.__error_code
3034

@@ -37,7 +41,6 @@ def get_as_dict(self):
3741
'status_code': self.get_status_code(),
3842
'traceback': "".join(traceback.format_exception(self))
3943
}
40-
44+
4145
def __str__(self):
4246
return f"{self.get_error_code()} - {self.get_error_source()} - {self.get_internal_message()} - {self.get_user_message()}"
43-

app/src/zcs/core/logger/logger.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import json
22
import logging
3+
import time
4+
from logging import LogRecord
35

46
from zcs.core.exception import ZcsException
7+
from zcs.core.session import request_context, RequestState
58

69
old_factory = logging.getLogRecordFactory()
710

@@ -31,6 +34,9 @@ def record_factory(*args, **kwargs):
3134
class CloudJsonFormatter(logging.Formatter):
3235
def format(self, record):
3336

37+
# FIXME
38+
# log op_code, request_id and time deltas
39+
3440
message = record.getMessage()
3541
if record.exc_info and record.original_exception:
3642
message = str(record.original_exception)
@@ -79,8 +85,19 @@ def __init__(self, verbose=False):
7985
logging.CRITICAL: logging.Formatter(set_bold_red + base_format + reset)
8086
}
8187

82-
def format(self, record):
88+
def format(self, record: LogRecord) -> str:
8389
formatter = self.FORMATTERS.get(record.levelno)
90+
91+
# Retrieve request state from request context
92+
request_state: RequestState = request_context.get()
93+
if request_state:
94+
time_ns = time.perf_counter_ns()
95+
delta_ns = time_ns - request_state.getCheckpointNs()
96+
total_ns = time_ns - request_state.getRequestStartNs()
97+
request_state.setCheckpointNs(time_ns)
98+
record.msg = "[OP_CODE:{}][REQ_ID:{}] {} ({:.2f}s delta - {:.2f}s total)".format(
99+
request_state.getOpCode(), request_state.getRequestId(), record.msg, delta_ns / 1e9, total_ns / 1e9)
100+
84101
return formatter.format(record)
85102

86103

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .request_context import request_context
2+
from .request_state import RequestState
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import contextvars
2+
3+
request_context = contextvars.ContextVar("request_state", default=None)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import time
2+
import uuid
3+
from typing import Optional
4+
5+
6+
class RequestState():
7+
8+
def __init__(
9+
self,
10+
request_id: Optional[str] = None,
11+
prefix: Optional[str] = None,
12+
op_code: Optional[str] = None):
13+
14+
self.__request_id = request_id if request_id else RequestState.generate_op_code(prefix=prefix)
15+
self.__op_code = op_code if op_code else self.__request_id
16+
self.__request_start_ns = time.perf_counter_ns()
17+
self.__checkpoint_ns = self.__request_start_ns
18+
19+
def getOpCode(self) -> str:
20+
"""
21+
Get operation code value.
22+
"""
23+
24+
return self.__op_code
25+
26+
def getRequestId(self) -> str:
27+
"""
28+
Get request id value.
29+
"""
30+
31+
return self.__request_id
32+
33+
def getRequestStartNs(self) -> int:
34+
"""
35+
Get request start time in nanoseconds.
36+
"""
37+
38+
return self.__request_start_ns
39+
40+
def getCheckpointNs(self) -> int:
41+
"""
42+
Get checkpoint time in nanoseconds.
43+
"""
44+
45+
return self.__checkpoint_ns
46+
47+
def setCheckpointNs(self, time_ns: int):
48+
"""
49+
Set checkpoint time in nanoseconds.
50+
"""
51+
52+
self.__checkpoint_ns = time_ns
53+
54+
@staticmethod
55+
def generate_op_code(prefix: Optional[str] = None) -> str:
56+
"""
57+
Generate a new unique operation code.
58+
If prefix is provided, prepend it to the generated code.
59+
"""
60+
op_code = str(uuid.uuid4())
61+
if prefix:
62+
return f"{prefix}_{op_code}"
63+
return op_code

0 commit comments

Comments
 (0)