Skip to content

Commit cbc6c95

Browse files
Common Logger Initialisation Improvements (#3)
* Generic initialise to handle file handlers * Template for pull requests
1 parent d384391 commit cbc6c95

File tree

4 files changed

+164
-36
lines changed

4 files changed

+164
-36
lines changed

.github/pull_request_template.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Jira:
2+
3+
[SEN-4452](https://simpplr.atlassian.net/browse/SEN-4452)
4+
5+
## Description:
6+
7+
Please include a summary of the changes in bullet points here.
8+
Also add screenshots if available.
9+
10+
## Type of change:
11+
12+
Please delete options that are not relevant.
13+
- [ ] Bug fix (non-breaking change which fixes an issue)
14+
- [ ] New feature (non-breaking change which adds functionality)
15+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
16+
17+
## Checklist (put x in bracket to check):
18+
19+
Please do not delete options here.
20+
- [ ] I have performed a self-review of my code
21+
- [ ] Existing unit tests pass locally with my changes
22+
- [ ] I have written coverage for new code
23+
- [ ] I have updated the [Production deployment](https://docs.google.com/spreadsheets/d/1_tc_7h3yPxScUb2JvZyxG0qx8Dl36bP7Fh-86_0kI1A) sheet if this service required production changes in this sprint

python_common_logger/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@
1515
Logger.initialise_console_logger(logger_name, service_name, level=logging.WARNING, context_config=None)
1616
ContextHandler.get_thread_execution_context(key='execution_context')
1717
ContextHandler.update_execution_context(execution_context, key='execution_context', reset=False)
18+
LoggerUtils.create_stream_handler
19+
LoggerUtils.create_json_formatter
20+
LoggerUtils.create_simple_handler
21+
LoggerUtils.create_simple_formatter
1822
"""
1923

2024
from .src import logger as Logger
25+
from .src.utils import logger as LoggerUtils
2126
from .src.context import context_handler as ContextHandler
2227
from .src.django import middleware as DjangoMiddleware
2328

python_common_logger/src/logger.py

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import logging
22
import json
3-
import sys
3+
4+
from logging import Handler, StreamHandler, Formatter, Filter
45

56
from .context.context_handler import get_thread_execution_context
67
from .context.execution_context import ExecutionContext, ExecutionContextType
78

89
from .constants.logger import LoggerKeys, LoggerContextConfigKeys
910
from .constants.context import ExecutionContextType
11+
from .utils.logger import create_json_formatter, create_stream_handler
1012

11-
class ContextFilter(logging.Filter):
13+
class ContextFilter(Filter):
1214
"""
1315
Log filter to extract the execution context from thread locals and populate it in the log record
1416
"""
@@ -27,6 +29,7 @@ def filter(self, record):
2729

2830
def initialise_console_logger(logger_name, service_name, level=logging.WARNING, context_config=None):
2931
"""
32+
@deprecated - use initialise_logger instead
3033
Initialises the logger with the handler, formatter and filter to log context data along with message
3134
in JSON format on the console.
3235
@@ -36,6 +39,25 @@ def initialise_console_logger(logger_name, service_name, level=logging.WARNING,
3639
level (int, optional): Log level. Defaults to logging.WARNING.
3740
context_config (dict, optional): Context config to configure logging parameters See LoggerContextConfigKeys for list of allowed params. Defaults to None.
3841
42+
Returns:
43+
Logger: Initialised logger
44+
"""
45+
return initialise_logger(logger_name, service_name, level, context_config)
46+
47+
48+
def initialise_logger(logger_name, service_name, level=logging.INFO, context_config=None, use_default_json_handler=True, other_handlers: [Handler]=[], propagate_to_parent=False):
49+
"""
50+
Initialises the logger.
51+
52+
Args:
53+
logger_name (string): Name of the logger to be initialised
54+
service_name (string): Service name that appears as the source in the logs
55+
level (int, optional): Log level. Defaults to logging.INFO.
56+
context_config (dict, optional): Context config to configure formatter for logging parameters. See LoggerContextConfigKeys for list of allowed params. Useless if use_default_json_handler is False. Defaults to None.
57+
use_default_json_handler (boolean, optional): Use the default JSON logger. Defaults to True.
58+
other_handlers ([Handler], optional): Handlers to be attached to the logger. Defaults to [].
59+
propagate_to_parent (boolean, optional): Should the log be propagated to parent. Defaults to False.
60+
3961
Returns:
4062
Logger: Initialised logger
4163
"""
@@ -44,43 +66,20 @@ def initialise_console_logger(logger_name, service_name, level=logging.WARNING,
4466
# Skip if already initialised. Helps preventing re-initialisation as Lambda instances share the logger instance.
4567
if hasattr(logger, 'initialized'):
4668
return logger
47-
48-
# Create handlers
49-
log_handler = logging.StreamHandler(sys.stdout)
50-
51-
log_format = {
52-
"source": f"{service_name}",
53-
"time": "%(asctime)s",
54-
"log": {
55-
"message": "%(message)s"
56-
},
57-
"logLevel": "%(levelname)s"
58-
}
59-
60-
if not context_config:
61-
context_config = {}
62-
63-
if not context_config.get(LoggerContextConfigKeys.DISABLE_CID.value):
64-
log_format[LoggerKeys.CORRELATION_ID.value] = f"%({ExecutionContextType.CORRELATION_ID.value})s"
65-
66-
if not context_config.get(LoggerContextConfigKeys.DISABLE_TID.value):
67-
log_format[LoggerKeys.TENANT_ID.value] = f"%({ExecutionContextType.TENANT_ID.value})s"
68-
69-
if not context_config.get(LoggerContextConfigKeys.DISABLE_UID.value):
70-
log_format[LoggerKeys.USER_ID.value] = f"%({ExecutionContextType.USER_ID.value})s"
71-
72-
# Create formatters and add it to handlers
73-
log_formatter = logging.Formatter(json.dumps(log_format), datefmt='%Y-%m-%dT%H:%M:%S%z')
74-
log_handler.setFormatter(log_formatter)
75-
76-
# Populate Context Filter in Record
77-
log_handler.addFilter(ContextFilter())
7869

79-
logger.addHandler(log_handler)
70+
if use_default_json_handler:
71+
json_formatter: Formatter = create_json_formatter(service_name, context_config)
72+
73+
json_handler: Handler = create_stream_handler(json_formatter, ContextFilter())
74+
75+
logger.addHandler(json_handler)
76+
77+
for handler in other_handlers:
78+
logger.addHandler(handler)
8079

8180
logger.setLevel(level)
8281

83-
logger.propagate = False
82+
logger.propagate = propagate_to_parent
8483
setattr(logger, 'initialized', True)
8584

86-
return logger
85+
return logger
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import logging
2+
import json
3+
import sys
4+
5+
from logging import Handler, StreamHandler, Formatter, Filter
6+
from ..constants.logger import LoggerKeys, LoggerContextConfigKeys
7+
from ..constants.context import ExecutionContextType
8+
9+
DEFAULT_DATE_FORMAT='%Y-%m-%dT%H:%M:%S%z'
10+
11+
def create_stream_handler(formatter: Formatter, filter: Filter=None) -> StreamHandler:
12+
"""
13+
Create a custom stream handler
14+
15+
Args:
16+
formatter (Formatter): Formatter for the handler
17+
filter (Filter, optional): Filter for the Handler. Defaults to None.
18+
19+
Returns:
20+
StreamHandler: Initialised Stream Handler
21+
"""
22+
log_handler: StreamHandler = logging.StreamHandler(sys.stdout)
23+
24+
if filter:
25+
log_handler.addFilter(filter)
26+
27+
log_handler.setFormatter(formatter)
28+
29+
return log_handler
30+
31+
def create_json_formatter(service_name: str, context_config:dict=None, date_format:str=DEFAULT_DATE_FORMAT) -> Formatter:
32+
"""
33+
Create a custom JSON Formatter
34+
35+
Args:
36+
service_name (string): Service Name
37+
context_config (dict, optional): Context data config. Defaults to None.
38+
date_format (str, optional): Date format for the logs. Defaults to DEFAULT_DATE_FORMAT.
39+
40+
Returns:
41+
Formatter: Log formatter
42+
"""
43+
log_format = {
44+
"source": f"{service_name}",
45+
"time": "%(asctime)s",
46+
"log": {
47+
"message": "[%(filename)s:%(funcName)s:%(lineno)s] %(message)s"
48+
},
49+
"logLevel": "%(levelname)s"
50+
}
51+
52+
if not context_config:
53+
context_config = {}
54+
55+
if not context_config.get(LoggerContextConfigKeys.DISABLE_CID.value):
56+
log_format[LoggerKeys.CORRELATION_ID.value] = f"%({ExecutionContextType.CORRELATION_ID.value})s"
57+
58+
if not context_config.get(LoggerContextConfigKeys.DISABLE_TID.value):
59+
log_format[LoggerKeys.TENANT_ID.value] = f"%({ExecutionContextType.TENANT_ID.value})s"
60+
61+
if not context_config.get(LoggerContextConfigKeys.DISABLE_UID.value):
62+
log_format[LoggerKeys.USER_ID.value] = f"%({ExecutionContextType.USER_ID.value})s"
63+
64+
log_formatter = logging.Formatter(json.dumps(log_format), datefmt=date_format)
65+
66+
return log_formatter
67+
68+
def create_simple_handler(date_format:str=DEFAULT_DATE_FORMAT) -> StreamHandler:
69+
"""
70+
Creates a Simple Handler.
71+
Can be used for dev / local testing, for better readability.
72+
73+
Args:
74+
date_format (str, optional): Date format for the logs. Defaults to DEFAULT_DATE_FORMAT.
75+
76+
Returns:
77+
StreamHandler: Log Handler
78+
"""
79+
log_formatter = create_simple_formatter(date_format)
80+
81+
log_handler = create_stream_handler(log_formatter)
82+
83+
return log_handler
84+
85+
def create_simple_formatter(date_format:str=DEFAULT_DATE_FORMAT) -> Formatter:
86+
"""
87+
Creates a Simple Formatter.
88+
Can be used for dev / local testing, for better readability.
89+
90+
Args:
91+
date_format (str, optional): Date format for the logs. Defaults to DEFAULT_DATE_FORMAT.
92+
93+
Returns:
94+
Formatter: Log formatter
95+
"""
96+
log_format = "%(asctime)s %(levelname)s [%(filename)s:%(funcName)s:%(lineno)s] %(message)s"
97+
98+
log_formatter = logging.Formatter(log_format, datefmt=date_format)
99+
100+
return log_formatter
101+

0 commit comments

Comments
 (0)