Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/publish-tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ jobs:
service_account: github-actions-sa@ai-accounting-405809.iam.gserviceaccount.com
access_token_lifetime: 600s

- name: Set application version
run: |
sed -i 's|version = "\(.*\)"|version = "${{ github.ref_name }}"|g' app/pyproject.toml &&
cat app/pyproject.toml

- name: Setup Python
uses: actions/setup-python@v3

Expand Down
2 changes: 1 addition & 1 deletion app/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

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

[tool.setuptools.packages.find]
Expand Down
19 changes: 11 additions & 8 deletions app/src/zcs/core/exception/exception.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import random
import string
import traceback
from typing import Optional


class ZcsException(Exception):

def __init__(self, user_message = "Unknown error.", error_source = "Unspecified", status_code = 500, internal_message = None):
def __init__(
self, user_message: str = "Unknown error.", error_source: str = "Unspecified",
status_code: int = 500, internal_message: Optional[str] = None):
super().__init__(user_message)

self.__error_source = error_source
self.__status_code = status_code
self.__user_message = user_message
Expand All @@ -15,16 +19,16 @@ def __init__(self, user_message = "Unknown error.", error_source = "Unspecified"

def get_status_code(self):
return self.__status_code

def get_error_source(self):
return self.__error_source

def get_user_message(self):
return self.__user_message

def get_internal_message(self):
return self.__internal_message

def get_error_code(self):
return self.__error_code

Expand All @@ -37,7 +41,6 @@ def get_as_dict(self):
'status_code': self.get_status_code(),
'traceback': "".join(traceback.format_exception(self))
}

def __str__(self):
return f"{self.get_error_code()} - {self.get_error_source()} - {self.get_internal_message()} - {self.get_user_message()}"

19 changes: 18 additions & 1 deletion app/src/zcs/core/logger/logger.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import json
import logging
import time
from logging import LogRecord

from zcs.core.exception import ZcsException
from zcs.core.session import request_context, RequestState

old_factory = logging.getLogRecordFactory()

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

# FIXME
# log op_code, request_id and time deltas

message = record.getMessage()
if record.exc_info and record.original_exception:
message = str(record.original_exception)
Expand Down Expand Up @@ -79,8 +85,19 @@ def __init__(self, verbose=False):
logging.CRITICAL: logging.Formatter(set_bold_red + base_format + reset)
}

def format(self, record):
def format(self, record: LogRecord) -> str:
formatter = self.FORMATTERS.get(record.levelno)

# Retrieve request state from request context
request_state: RequestState = request_context.get()
if request_state:
time_ns = time.perf_counter_ns()
delta_ns = time_ns - request_state.getCheckpointNs()
total_ns = time_ns - request_state.getRequestStartNs()
request_state.setCheckpointNs(time_ns)
record.msg = "[OP_CODE:{}][REQ_ID:{}] {} ({:.2f}s delta - {:.2f}s total)".format(
request_state.getOpCode(), request_state.getRequestId(), record.msg, delta_ns / 1e9, total_ns / 1e9)

return formatter.format(record)


Expand Down
2 changes: 2 additions & 0 deletions app/src/zcs/core/session/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .request_context import request_context
from .request_state import RequestState
3 changes: 3 additions & 0 deletions app/src/zcs/core/session/request_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import contextvars

request_context = contextvars.ContextVar("request_state", default=None)
63 changes: 63 additions & 0 deletions app/src/zcs/core/session/request_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import time
import uuid
from typing import Optional


class RequestState():

def __init__(
self,
request_id: Optional[str] = None,
prefix: Optional[str] = None,
op_code: Optional[str] = None):

self.__request_id = request_id if request_id else RequestState.generate_op_code(prefix=prefix)
self.__op_code = op_code if op_code else self.__request_id
self.__request_start_ns = time.perf_counter_ns()
self.__checkpoint_ns = self.__request_start_ns

def getOpCode(self) -> str:
"""
Get operation code value.
"""

return self.__op_code

def getRequestId(self) -> str:
"""
Get request id value.
"""

return self.__request_id

def getRequestStartNs(self) -> int:
"""
Get request start time in nanoseconds.
"""

return self.__request_start_ns

def getCheckpointNs(self) -> int:
"""
Get checkpoint time in nanoseconds.
"""

return self.__checkpoint_ns

def setCheckpointNs(self, time_ns: int):
"""
Set checkpoint time in nanoseconds.
"""

self.__checkpoint_ns = time_ns

@staticmethod
def generate_op_code(prefix: Optional[str] = None) -> str:
"""
Generate a new unique operation code.
If prefix is provided, prepend it to the generated code.
"""
op_code = str(uuid.uuid4())
if prefix:
return f"{prefix}_{op_code}"
return op_code