Skip to content

Commit 7827794

Browse files
Merge pull request #1 from Simpplr/kthacker/init-commit
Kthacker/init commit
2 parents 48b914d + 437a314 commit 7827794

File tree

13 files changed

+374
-0
lines changed

13 files changed

+374
-0
lines changed

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
recursive-include python_common_logger *
2+
recursive-include src *
File renamed without changes.

python_common_logger/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
Python Common Logger to log in JSON format with context data.
3+
4+
Check: https://simpplr.atlassian.net/wiki/spaces/SA/pages/2467365251/Logging+Strategy
5+
6+
Classes:
7+
Logger.ContextFilter
8+
DjangoMiddleware.ContextMiddleware
9+
ContextConstants.ExecutionContextType
10+
LoggingConstants.LoggerContextConfigKeys
11+
LoggingConstants.LoggerKeys
12+
RequestConstants.RequestHeaderKeys
13+
14+
Functions:
15+
Logger.initialise_console_logger(logger_name, service_name, level=logging.WARNING, context_config=None)
16+
ContextHandler.get_thread_execution_context(key='execution_context')
17+
ContextHandler.update_execution_context(execution_context, key='execution_context', reset=False)
18+
"""
19+
20+
from .src import logger as Logger
21+
from .src.context import context_handler as ContextHandler
22+
from .src.django import middleware as DjangoMiddleware
23+
24+
from .src.constants import context as ContextConstants
25+
from .src.constants import logger as LoggingConstants
26+
from .src.constants import request as RequestConstants
27+
28+
from .src.context.execution_context import *
29+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from enum import Enum
2+
3+
class ExecutionContextType(Enum):
4+
"""
5+
ExecutionContextType
6+
7+
Attributes
8+
----------
9+
CORRELATION_ID : str
10+
Correlation Id
11+
TENANT_ID : str
12+
Tenant Id
13+
USER_ID : str
14+
User Id
15+
"""
16+
CORRELATION_ID = 'correlation_id'
17+
TENANT_ID = 'tenant_id'
18+
USER_ID = 'user_id'
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from enum import Enum
2+
3+
class LoggerContextConfigKeys(Enum):
4+
"""
5+
Context Config Keys
6+
7+
Attributes
8+
----------
9+
DISABLE_TID : str
10+
Disable Tenant Id
11+
DISABLE_UID : str
12+
Disable User Id
13+
DISABLE_CID : str
14+
Disable Correlation Id
15+
"""
16+
DISABLE_TID = 'disable_tid'
17+
DISABLE_UID = 'disable_uid'
18+
DISABLE_CID = 'disable_cid'
19+
20+
class LoggerKeys(Enum):
21+
"""
22+
Logging Key
23+
24+
Attributes
25+
----------
26+
CORRELATION_ID : str
27+
Correlation Id
28+
TENANT_ID : str
29+
Tenant Id
30+
USER_ID : str
31+
User Id
32+
"""
33+
CORRELATION_ID = 'cid'
34+
TENANT_ID = 'tid'
35+
USER_ID = 'uid'
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from enum import Enum
2+
3+
class RequestHeaderKeys(Enum):
4+
"""
5+
Header keys
6+
7+
Attributes
8+
----------
9+
ACCOUNT_ID : str
10+
Account Id / Tenant ID
11+
USER_ID : str
12+
User Id
13+
CORRELATION_ID : str
14+
Correlation Id
15+
"""
16+
ACCOUNT_ID = 'x-smtip-tid',
17+
USER_ID = 'x-smtip-uid',
18+
CORRELATION_ID = 'x-smtip-cid',
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from .execution_context import ExecutionContext
2+
3+
from threading import local
4+
5+
_locals = local()
6+
7+
def get_thread_execution_context(key='execution_context') -> ExecutionContext:
8+
"""
9+
Fetches the execution context from the thread local. If absent, initialises and returns an empty one.
10+
11+
Args:
12+
key (str, optional): key. Defaults to 'execution_context'.
13+
14+
Returns:
15+
ExecutionContext: Thread local execution context.
16+
"""
17+
return getattr(_locals, key, ExecutionContext({}))
18+
19+
def update_execution_context(execution_context: ExecutionContext, key='execution_context', reset=False) -> ExecutionContext:
20+
"""
21+
Updates the execution context.
22+
23+
Args:
24+
execution_context (ExecutionContext): Execution context to be updated.
25+
key (str, optional): key. Defaults to 'execution_context'.
26+
reset (bool, optional): Reset the entire context. Defaults to False.
27+
28+
Returns:
29+
ExecutionContext: Updated execution context
30+
"""
31+
current_execution_context: ExecutionContext = get_thread_execution_context()
32+
33+
current_execution_context.update(execution_context.get_context(), reset)
34+
35+
setattr(_locals, key, current_execution_context)
36+
37+
return current_execution_context
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import copy
2+
3+
from ..exceptions.validation_error import ValidationException
4+
from ..constants.context import ExecutionContextType
5+
6+
class ExecutionContext:
7+
"""
8+
Stores the Execution context and provides helper methods to manage it.
9+
10+
Methods
11+
-------
12+
update(execution_context:dict, reset:bool=False):
13+
Updates the Execution Context.
14+
15+
get_context_by_key(key:str):
16+
Returns the execution context stored for the provided key.
17+
18+
get_context() -> dict:
19+
Returns the entire execution context.
20+
21+
reset(key:str):
22+
Resets the context.
23+
"""
24+
ALLOWED_KEYS = [e.value for e in ExecutionContextType]
25+
26+
def __init__(self, execution_context: dict):
27+
self._context = {}
28+
for key in execution_context.keys():
29+
if key not in self.ALLOWED_KEYS:
30+
# TODO: Create Validation error
31+
raise ValidationException(f'Invalid execution context type: {key}')
32+
else:
33+
self._context[key] = execution_context[key]
34+
35+
def update(self, execution_context: dict, reset=False):
36+
"""
37+
Updates the execution context.
38+
39+
Args:
40+
execution_context (dict): Execution context to be updated.
41+
reset (bool, optional): If True, replaces the entire context. If False, updates only the provided values. Defaults to False.
42+
43+
Raises:
44+
ValidationException: If an invalid key is provided.
45+
"""
46+
if reset:
47+
self.reset()
48+
49+
for key in execution_context.keys():
50+
if key not in self.ALLOWED_KEYS:
51+
raise ValidationException(f'Invalid execution context type: {key}')
52+
else:
53+
self._context[key] = execution_context[key]
54+
55+
def get_context_by_key(self, key: str) -> str:
56+
"""
57+
Returns the execution context for the specified key
58+
59+
Args:
60+
key (str): key
61+
62+
Returns:
63+
any: Execution Context
64+
"""
65+
return copy.deepcopy(self._context[key])
66+
67+
def get_context(self) -> dict:
68+
"""
69+
Returns the execution context.
70+
71+
Returns:
72+
dict: Entire execution context
73+
"""
74+
return copy.deepcopy(self._context)
75+
76+
def reset(self):
77+
"""
78+
Resets the execution context.
79+
"""
80+
self._context = {}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from uuid import uuid4
2+
from ..context.context_handler import update_execution_context
3+
from ..context.execution_context import ExecutionContext
4+
5+
from ..constants.request import RequestHeaderKeys
6+
from ..constants.context import ExecutionContextType
7+
8+
class ContextMiddleware():
9+
"""
10+
Django Context Middle. Extracts the headers from the request and populates the execution context
11+
in thread local data.
12+
"""
13+
def __init__(self, get_response):
14+
self.get_response = get_response
15+
16+
def __call__(self, request):
17+
execution_context = {}
18+
19+
if RequestHeaderKeys.CORRELATION_ID.value in request.META:
20+
execution_context[ExecutionContextType.CORRELATION_ID.value] = request.META[RequestHeaderKeys.CORRELATION_ID.value]
21+
else:
22+
execution_context[ExecutionContextType.CORRELATION_ID.value] = uuid4()
23+
24+
if RequestHeaderKeys.ACCOUNT_ID.value in request.META:
25+
execution_context[ExecutionContextType.TENANT_ID.value] = request.META[RequestHeaderKeys.ACCOUNT_ID.value]
26+
27+
if RequestHeaderKeys.USER_ID.value in request.META:
28+
execution_context[ExecutionContextType.USER_ID.value] = request.META[RequestHeaderKeys.USER_ID.value]
29+
30+
update_execution_context(ExecutionContext(execution_context))
31+
32+
response = self.get_response(request)
33+
34+
return response
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class BaseException(Exception):
2+
"""
3+
A class used to represent an Exception with code
4+
5+
Attributes
6+
----------
7+
message : str
8+
Message to describe the exception
9+
code : int
10+
Represents the integer code. Usually the HTTP error code.
11+
"""
12+
def __init__(self, message, code=500):
13+
super().__init__(message)
14+
self.code = code

0 commit comments

Comments
 (0)