Skip to content

Added modulewise logger #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 25, 2025
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## _v2.0.0_

### **Date: 28-APRIL-2025**

- Custom logger support

## _v1.11.2_

### **Date: 21-APRIL-2025**
Expand Down
2 changes: 1 addition & 1 deletion contentstack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
__title__ = 'contentstack-delivery-python'
__author__ = 'contentstack'
__status__ = 'debug'
__version__ = 'v1.11.2'
__version__ = 'v2.0.0'
__endpoint__ = 'cdn.contentstack.io'
__email__ = '[email protected]'
__developer_email__ = '[email protected]'
Expand Down
6 changes: 2 additions & 4 deletions contentstack/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@
import logging
from urllib import parse

log = logging.getLogger(__name__)


class Asset:
r"""`Asset` refer to all the media files (images, videos, PDFs, audio files, and so on)."""

def __init__(self, http_instance, uid=None):
def __init__(self, http_instance, uid=None, logger=None):
self.http_instance = http_instance
self.asset_params = {}
self.__uid = uid
Expand All @@ -22,6 +19,7 @@ def __init__(self, http_instance, uid=None):
self.base_url = f'{self.http_instance.endpoint}/assets/{self.__uid}'
if 'environment' in self.http_instance.headers:
self.asset_params['environment'] = self.http_instance.headers['environment']
self.logger = logger or logging.getLogger(__name__)

def environment(self, environment):
r"""Provide the name of the environment if you wish to retrieve the assets published
Expand Down
6 changes: 2 additions & 4 deletions contentstack/assetquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,20 @@
from contentstack.basequery import BaseQuery
from contentstack.utility import Utils

log = logging.getLogger(__name__)


class AssetQuery(BaseQuery):
"""
This call fetches the list of all the assets of a particular stack.
"""

def __init__(self, http_instance):
def __init__(self, http_instance, logger=None):
super().__init__()
self.http_instance = http_instance
self.asset_query_params = {}
self.base_url = f"{self.http_instance.endpoint}/assets"
if "environment" in self.http_instance.headers:
env = self.http_instance.headers["environment"]
self.base_url = f"{self.base_url}?environment={env}"
self.logger = logger or logging.getLogger(__name__)

def environment(self, environment):
r"""Provide the name of the environment if you wish to retrieve the assets published
Expand Down
6 changes: 2 additions & 4 deletions contentstack/basequery.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import enum
import logging

log = logging.getLogger(__name__)


class QueryOperation(enum.Enum):
"""
QueryOperation is enum that Provides Options to perform operation to query the result.
Expand Down Expand Up @@ -38,9 +35,10 @@ class BaseQuery:
Common Query class works for Query As well as Asset
"""

def __init__(self):
def __init__(self, logger=None):
self.parameters = {}
self.query_params = {}
self.logger = logger or logging.getLogger(__name__)

def where(self, field_uid: str, query_operation: QueryOperation, fields=None):
"""
Expand Down
6 changes: 2 additions & 4 deletions contentstack/contenttype.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
from contentstack.entry import Entry
from contentstack.query import Query

log = logging.getLogger(__name__)


class ContentType:
"""
Content type defines the structure or schema of a page or a
Expand All @@ -26,10 +23,11 @@ class ContentType:
content type.
"""

def __init__(self, http_instance, content_type_uid):
def __init__(self, http_instance, content_type_uid, logger=None):
self.http_instance = http_instance
self.__content_type_uid = content_type_uid
self.local_param = {}
self.logger = logger or logging.getLogger(__name__)

def entry(self, entry_uid: str):
r"""
Expand Down
6 changes: 2 additions & 4 deletions contentstack/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
from contentstack.deep_merge_lp import DeepMergeMixin
from contentstack.entryqueryable import EntryQueryable

log = logging.getLogger(__name__)


class Entry(EntryQueryable):
"""
An entry is the actual piece of content that you want to publish.
Expand All @@ -23,14 +20,15 @@ class Entry(EntryQueryable):
locale={locale_code}
"""

def __init__(self, http_instance, content_type_uid, entry_uid):
def __init__(self, http_instance, content_type_uid, entry_uid, logger=None):
super().__init__()
EntryQueryable.__init__(self)
self.entry_param = {}
self.http_instance = http_instance
self.content_type_id = content_type_uid
self.entry_uid = entry_uid
self.base_url = self.__get_base_url()
self.logger = logger or logging.getLogger(__name__)

def environment(self, environment):
"""
Expand Down
6 changes: 2 additions & 4 deletions contentstack/entryqueryable.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@
"""
import logging

log = logging.getLogger(__name__)


class EntryQueryable:
"""
This class is base class for the Entry and Query class that shares common functions
"""

def __init__(self):
def __init__(self, logger=None):
self.entry_queryable_param = {}
self.logger = logger or logging.getLogger(__name__)

def locale(self, locale: str):
"""
Expand Down
3 changes: 0 additions & 3 deletions contentstack/https_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
import contentstack
from contentstack.controller import get_request

log = logging.getLogger(__name__)


def __get_os_platform():
os_platform = platform.system()
if os_platform == 'Darwin':
Expand Down
5 changes: 2 additions & 3 deletions contentstack/image_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@

import logging

log = logging.getLogger(__name__)


class ImageTransform: # pylint: disable=too-few-public-methods
"""
The Image Delivery API is used to retrieve, manipulate and/or convert image
files
"""

def __init__(self, http_instance, image_url, **kwargs):
def __init__(self, http_instance, image_url, logger=None, **kwargs):
"""
creates instance of the ImageTransform class
:param httpInstance: instance of HttpsConnection
Expand All @@ -35,6 +33,7 @@ def __init__(self, http_instance, image_url, **kwargs):
self.http_instance = http_instance
self.image_url = image_url
self.image_params = kwargs
self.logger = logger or logging.getLogger(__name__)

def get_url(self):
"""
Expand Down
5 changes: 2 additions & 3 deletions contentstack/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
from contentstack.deep_merge_lp import DeepMergeMixin
from contentstack.entryqueryable import EntryQueryable

log = logging.getLogger(__name__)


class QueryType(enum.Enum):
"""
Expand All @@ -40,7 +38,7 @@ class Query(BaseQuery, EntryQueryable):
>>> result = query.locale('locale-code').excepts('field_uid').limit(4).skip(5).find()
"""

def __init__(self, http_instance, content_type_uid):
def __init__(self, http_instance, content_type_uid, logger=None):
super().__init__()
EntryQueryable.__init__(self)
self.content_type_uid = content_type_uid
Expand All @@ -50,6 +48,7 @@ def __init__(self, http_instance, content_type_uid):
'You are not allowed here without content_type_uid')
self.base_url = f'{self.http_instance.endpoint}/content_types/{self.content_type_uid}/entries'
self.base_url = self.__get_base_url()
self.logger = logger or logging.getLogger(__name__)

def __get_base_url(self, endpoint=''):
if endpoint is not None and endpoint.strip(): # .strip() removes leading/trailing whitespace
Expand Down
4 changes: 2 additions & 2 deletions contentstack/stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from contentstack.https_connection import HTTPSConnection
from contentstack.image_transform import ImageTransform

log = logging.getLogger(__name__)
DEFAULT_HOST = 'cdn.contentstack.io'


Expand Down Expand Up @@ -42,6 +41,7 @@ def __init__(self, api_key: str, delivery_token: str, environment: str,
live_preview=None,
branch=None,
early_access = None,
logger=None,
):
"""
# Class that wraps the credentials of the authenticated user. Think of
Expand Down Expand Up @@ -78,7 +78,7 @@ def __init__(self, api_key: str, delivery_token: str, environment: str,
live_preview={enable=True, authorization='your auth token'}, retry_strategy= _strategy)
```
"""
logging.basicConfig(level=logging.DEBUG)
self.logger = logger or logging.getLogger(__name__)
self.headers = {}
self._query_params = {}
self.sync_param = {}
Expand Down
101 changes: 59 additions & 42 deletions contentstack/utility.py
Original file line number Diff line number Diff line change
@@ -1,76 +1,93 @@
"""
Utility functions for logging and URL manipulation.
Last modified by ishaileshmishra on 06/08/20.
Copyright 2019 Contentstack. All rights reserved.
"""

import json
import logging
from urllib.parse import urlencode, urljoin
from urllib.parse import urlencode

log = logging.getLogger(__name__)


def config_logging(logging_type: logging.WARNING):
def setup_logging(logging_type=logging.INFO, filename='app.log'):
"""
This is to create logging config
:param logging_type: Level of the logging
:return: basicConfig instance
Global one-time logging configuration.
Should be called from your main application entry point.
"""
logging.basicConfig(
filename='app.log',
filename=filename,
level=logging_type,
format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
datefmt='%H:%M:%S'
datefmt='%Y-%m-%d %H:%M:%S'
)


class Utils:

@staticmethod
def config_logging():
""" Setting up logging """
logging.basicConfig(
filename='report_log.log',
format='%(asctime)s - %(message)s',
level=logging.INFO
)
def setup_logger(name="AppLogger", level=logging.INFO, filename='app.log'):
"""
Creates and configures a named logger with file and console output.
Prevents duplicate handlers.
"""
logger = logging.getLogger(name)
if not logger.handlers:
logger.setLevel(level)

@staticmethod
def setup_logger():
"""setup logger for the application"""
return logging.getLogger("Config")
formatter = logging.Formatter(
'[%(asctime)s] %(levelname)s - %(name)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)

file_handler = logging.FileHandler(filename)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

return logger

@staticmethod
def log(message):
"""this generates log message"""
logging.debug(message)
def log(message, level=logging.DEBUG):
"""
Log a message with the specified level.
Default is DEBUG.
"""
logger = logging.getLogger("AppLogger")
logger.log(level, message)

@staticmethod
def do_url_encode(params):
"""
To encode url with query parameters
:param params:
:return: encoded url
Encode query parameters to URL-safe format.
:param params: Dictionary of parameters
:return: Encoded URL query string
"""
return parse.urlencode(params)
if not isinstance(params, dict):
raise ValueError("params must be a dictionary")
return urlencode(params, doseq=True)

@staticmethod
def get_complete_url(base_url: str, params: dict) -> str:
def get_complete_url(base_url: str, params: dict, skip_encoding=False) -> str:
"""
Creates a complete URL using base_url and their respective parameters.
:param base_url: The base URL to which parameters are appended.
:param params: A dictionary of parameters to be included in the URL.
:return: A complete URL with encoded parameters.
Construct a full URL by combining base URL and encoded parameters.
Handles JSON stringification for the `query` key.
:param base_url: Base API URL
:param params: Dictionary of query parameters
:param skip_encoding: Set True to skip URL encoding
:return: Complete URL
"""
# Ensure 'query' is properly serialized as a JSON string without extra quotes
if 'query' in params:
if not isinstance(base_url, str) or not isinstance(params, dict):
raise ValueError("base_url must be a string and params must be a dictionary")

if 'query' in params and not skip_encoding:
params["query"] = json.dumps(params["query"], separators=(',', ':'))

# Encode parameters
query_string = urlencode(params, doseq=True)

# Join base_url and query_string
if '?' in base_url:
return f'{base_url}&{query_string}'
if not skip_encoding:
query_string = urlencode(params, doseq=True)
else:
return f'{base_url}?{query_string}'
query_string = "&".join(f"{k}={v}" for k, v in params.items())

# Append with appropriate separator
return f'{base_url}&{query_string}' if '?' in base_url else f'{base_url}?{query_string}'
Loading
Loading