Repository: Project-Stage-Academy/UA1385_Python_PS
Pull request #95: Integrated logging system #54
Author: login: HlibSokol
Created: 2025-05-20T12:50:30Z | Merged: 2025-05-26T12:11:35Z

- configured loggers, handlers and formatters in settings.py 
- added ExampleView in /users/views.py to check if logging works properly, also added urls that were needed for test
- added commented tips to every views.py how logging should look like

Due to impossibility of integrating normally functioning logging system because of not written views yet on this stage, I have left one ExampleView I used for testing. There is no need of this view in future so it can be deleted. Also, to check if log system works, you need to uncomment path in /forum/forum/urls.py to "users.urls" and open http://localhost:8000/logtest/ on your browser - log strings have to appear in console and appropriate file in logs/ folder (auto-creates when local server starts and such directory doesn't already exist)
![image](https://github.com/user-attachments/assets/717cd889-8b26-4d7c-94f0-ae8611ace151)
![image](https://github.com/user-attachments/assets/e008db7e-8db5-458a-9755-fb9f19323195)
![image](https://github.com/user-attachments/assets/d311c32e-dc77-4765-bacf-3993853938d2)




Diff:
diff --git a/.gitignore b/.gitignore
index 9886b92..ba51029 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ MANIFEST
 # Installer logs
 pip-log.txt
 pip-delete-this-directory.txt
+logs/
 
 # Unit test / coverage reports
 htmlcov/
@@ -160,4 +161,4 @@ cython_debug/
 #.idea/
 
 # static files
-staticfiles/
\ No newline at end of file
+staticfiles/
diff --git a/forum/communications/views.py b/forum/communications/views.py
index 91ea44a..920a38a 100644
--- a/forum/communications/views.py
+++ b/forum/communications/views.py
@@ -1,3 +1,17 @@
 from django.shortcuts import render
+import logging
+
+logger = logging.getLogger(__name__)
 
 # Create your views here.
+
+# # example of logging calls for future views
+#
+# def example_view(self, request):
+#     try:
+#         # some processing/actions with data
+#         logger.info(f"Processing request")
+#         # returning responce
+#     except Exception as e:
+#         logger.error(f"Error occured: {e}")
+#         # returning responce
diff --git a/forum/dashboard/views.py b/forum/dashboard/views.py
index 91ea44a..7862a10 100644
--- a/forum/dashboard/views.py
+++ b/forum/dashboard/views.py
@@ -1,3 +1,17 @@
 from django.shortcuts import render
+import logging
+
+logger = logging.getLogger(__name__)
 
 # Create your views here.
+
+# # example of logging calls for future views
+#
+# def example_view(self, request):
+#     try:
+#         # some processing/actions with data
+#         logger.info(f"Processing request")
+#         # returning responce
+#     except Exception as e:
+#         logger.error(f"Error occured: {e}")
+#         # returning responce
\ No newline at end of file
diff --git a/forum/forum/settings.py b/forum/forum/settings.py
index 2b324e0..fedd252 100644
--- a/forum/forum/settings.py
+++ b/forum/forum/settings.py
@@ -129,3 +129,86 @@
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
 AUTH_USER_MODEL = "users.User"
+
+LOG_DIR = os.path.join(BASE_DIR, 'logs')
+os.makedirs(LOG_DIR, exist_ok=True)
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '{asctime} [{levelname}] {name} → {message}',
+            'style': '{',
+        },
+        'simple': {
+            'format': '[{levelname}] {message}',
+            'style': '{',
+        },
+    },
+    'handlers': {
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'formatter': 'simple',
+        },
+        'file': {
+            'level': 'INFO',
+            'class': 'logging.handlers.TimedRotatingFileHandler',
+            'filename': os.path.join(LOG_DIR, 'project.log'),
+            'when': 'midnight',
+            'backupCount': 7,
+            'formatter': 'verbose',
+        },
+        'file_users': {
+            'level': 'DEBUG',
+            'class': 'logging.FileHandler',
+            'filename': os.path.join(LOG_DIR, 'users.log'),
+            'formatter': 'verbose',
+        },
+        'file_communications': {
+            'level': 'DEBUG',
+            'class': 'logging.FileHandler',
+            'filename': os.path.join(LOG_DIR, 'communications.log'),
+            'formatter': 'verbose',
+        },
+        'file_dashboard': {
+            'level': 'DEBUG',
+            'class': 'logging.FileHandler',
+            'filename': os.path.join(LOG_DIR, 'dashboard.log'),
+            'formatter': 'verbose',
+        },
+        'file_profiles': {
+            'level': 'DEBUG',
+            'class': 'logging.FileHandler',
+            'filename': os.path.join(LOG_DIR, 'profiles.log'),
+            'formatter': 'verbose',
+        },
+        'file_projects': {
+            'level': 'DEBUG',
+            'class': 'logging.FileHandler',
+            'filename': os.path.join(LOG_DIR, 'projects.log'),
+            'formatter': 'verbose',
+        }
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['console', 'file'],
+            'level': 'DEBUG',
+        },
+        'django.db.backends': {
+            'handlers': ['console', 'file'],
+            'level': 'INFO',
+            'propagate': False,
+        },
+        'users': {'handlers': ['console', 'file_users'], 'level': 'DEBUG'},
+        'profiles': {'handlers': ['console', 'file_profiles'], 'level': 'DEBUG'},
+        'projects': {'handlers': ['console', 'file_projects'], 'level': 'DEBUG'},
+        'communications': {'handlers': ['console', 'file_communications'], 'level': 'DEBUG'},
+        'dashboard': {'handlers': ['console', 'file_dashboard'], 'level': 'DEBUG'},
+    },
+    'root': {
+            'handlers': ['console', 'file'],
+            'level': 'WARNING',
+    },
+}
\ No newline at end of file
diff --git a/forum/profiles/views.py b/forum/profiles/views.py
index 91ea44a..7862a10 100644
--- a/forum/profiles/views.py
+++ b/forum/profiles/views.py
@@ -1,3 +1,17 @@
 from django.shortcuts import render
+import logging
+
+logger = logging.getLogger(__name__)
 
 # Create your views here.
+
+# # example of logging calls for future views
+#
+# def example_view(self, request):
+#     try:
+#         # some processing/actions with data
+#         logger.info(f"Processing request")
+#         # returning responce
+#     except Exception as e:
+#         logger.error(f"Error occured: {e}")
+#         # returning responce
\ No newline at end of file
diff --git a/forum/projects/views.py b/forum/projects/views.py
index 91ea44a..7862a10 100644
--- a/forum/projects/views.py
+++ b/forum/projects/views.py
@@ -1,3 +1,17 @@
 from django.shortcuts import render
+import logging
+
+logger = logging.getLogger(__name__)
 
 # Create your views here.
+
+# # example of logging calls for future views
+#
+# def example_view(self, request):
+#     try:
+#         # some processing/actions with data
+#         logger.info(f"Processing request")
+#         # returning responce
+#     except Exception as e:
+#         logger.error(f"Error occured: {e}")
+#         # returning responce
\ No newline at end of file
diff --git a/forum/users/permissions.py b/forum/users/permissions.py
index e69de29..8eef261 100644
--- a/forum/users/permissions.py
+++ b/forum/users/permissions.py
@@ -0,0 +1,13 @@
+import logging
+from rest_framework.permissions import BasePermission
+
+logger = logging.getLogger(__name__)
+
+# # example of logging calls for future permissions
+
+# class CustomPermission(BasePermission):
+#     def has_permission(self, request, view):
+#         if some_condition_fails:
+#             logger.warning(f"Permission denied for user {request.user}")
+#             return False
+#         return True
\ No newline at end of file
diff --git a/forum/users/views.py b/forum/users/views.py
index 44bb01d..c6b11df 100644
--- a/forum/users/views.py
+++ b/forum/users/views.py
@@ -2,11 +2,19 @@
 from rest_framework.response import Response
 from rest_framework import status
 from .serializers import UserRegisterSerializer
+import logging
+
+logger = logging.getLogger(__name__)
 
 class RegisterView(APIView):
     def post(self, request):
+        logger.info("Received registration request")
+
         serializer = UserRegisterSerializer(data=request.data)
         if serializer.is_valid():
             user = serializer.save()
+            logger.info(f"New user registered successfully: id={user.user_id}, email={user.email}")
             return Response({"message": "User created successfully"}, status=status.HTTP_201_CREATED)
-        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
\ No newline at end of file
+        
+        logger.warning(f"User registration failed. Errors: {serializer.errors}")
+        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


Reviews:
user: login: ArsenStrus
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-20T15:39:50Z
user: login: VovaD199
body: Overall, the work is well done, sometimes even too much, I commented on the shortcomings found and plan to discuss them in more detail during the meeting, now I consider it necessary to implement this functionality, thank you for your work
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-20T20:12:48Z
user: login: mehalyna
body: ### Optional!
Using a `logging.yaml` file makes your logging configuration cleaner and easier to maintain — especially for larger projects.
**1: Create `logging.yaml` in your Django project root**

```
# logging.yaml
version: 1
disable_existing_loggers: False

formatters:
  verbose:
    format: "{asctime} [{levelname}] {name} → {message}"
    style: "{"
  simple:
    format: "[{levelname}] {message}"
    style: "{"

handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout

  file:
    class: logging.handlers.TimedRotatingFileHandler
    level: INFO
    formatter: verbose
    filename: logs/project.log
    when: midnight
    backupCount: 7
    encoding: utf8

loggers:
  django:
    handlers: [console, file]
    level: DEBUG

  django.db.backends:
    handlers: [console, file]
    level: INFO
    propagate: False

  users:
    handlers: [console, file]
    level: DEBUG

  profiles:
    handlers: [console, file]
    level: DEBUG

  projects:
    handlers: [console, file]
    level: DEBUG

  communications:
    handlers: [console, file]
    level: DEBUG

  dashboard:
    handlers: [console, file]
    level: DEBUG

root:
  handlers: [console, file]
  level: WARNING
```
**2: Load it in your `settings.py`**
Add this near the bottom of your `settings.py`:

```
import logging.config
import yaml

LOG_DIR = os.path.join(BASE_DIR, 'logs')
os.makedirs(LOG_DIR, exist_ok=True)

with open(os.path.join(BASE_DIR, 'logging.yaml'), 'r') as f:
    config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)
```

> Make sure `pyyaml` is installed:  
```
pip install pyyaml
```
**Benefits**
- Modular, human-readable configuration.
- Easy to switch environments or adjust settings without touching `settings.py`.
- Plays well with Docker and CI pipelines.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T05:57:41Z
user: login: HlibSokol
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T14:06:06Z
user: login: HlibSokol
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T14:06:26Z
user: login: HlibSokol
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T14:18:36Z
user: login: IrinaKrivoruchko
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-22T14:39:29Z
user: login: mehalyna
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T11:40:11Z

Review comments:
path: forum/users/permissions.py
user: login: VovaD199
body: forum/users/permissions.py
why is the logging example in this file and not in views
created_at: 2025-05-20T20:08:19Z
author_association: COLLABORATOR
path: forum/users/views.py
user: login: VovaD199
body: the code example should be fully commented out with a multiline comment, not a docstring. The docstring should describe the functionality of the object. code that can be removed should be removed
created_at: 2025-05-20T20:09:06Z
author_association: COLLABORATOR
diff_hunk: @@ -133,3 +133,53 @@
 # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+LOG_DIR = os.path.join(BASE_DIR, 'logs')
+if not os.path.exists(LOG_DIR):
+    os.makedirs(LOG_DIR)
path: forum/forum/settings.py
user: login: mehalyna
body: Use `os.makedirs(LOG_DIR, exist_ok=True)` instead — safer for concurrency
created_at: 2025-05-21T05:48:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -133,3 +133,53 @@
 # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+LOG_DIR = os.path.join(BASE_DIR, 'logs')
+if not os.path.exists(LOG_DIR):
+    os.makedirs(LOG_DIR)
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '{asctime} [{levelname}] {name} → {message}',
+            'style': '{',
+        },
+        'simple': {
+            'format': '[{levelname}] {message}',
+            'style': '{',
+        },
+    },
+    'handlers': {
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'formatter': 'simple',
+        },
+        'file': {
+            'level': 'INFO',
+            'class': 'logging.handlers.TimedRotatingFileHandler',
+            'filename': os.path.join(LOG_DIR, 'project.log'),
+            'when': 'midnight',
+            'backupCount': 7,
+            'formatter': 'verbose',
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['console', 'file'],
+            'level': 'DEBUG',
+        },
+        'django.db.backends': {
+            'handlers': ['console', 'file'],
+            'level': 'INFO',
+            'propagate': False,
+        },
+        'users': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+        'profiles': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+        'projects': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+        'communications': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+        'dashboard': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+    },
path: forum/forum/settings.py
user: login: mehalyna
body: If any module logs without a defined logger, the message might get lost.
Add:
```
'root': {
    'handlers': ['console', 'file'],
    'level': 'WARNING',
}
```
created_at: 2025-05-21T05:49:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -133,3 +133,53 @@
 # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+LOG_DIR = os.path.join(BASE_DIR, 'logs')
+if not os.path.exists(LOG_DIR):
+    os.makedirs(LOG_DIR)
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '{asctime} [{levelname}] {name} → {message}',
+            'style': '{',
+        },
+        'simple': {
+            'format': '[{levelname}] {message}',
+            'style': '{',
+        },
+    },
+    'handlers': {
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'formatter': 'simple',
+        },
+        'file': {
+            'level': 'INFO',
+            'class': 'logging.handlers.TimedRotatingFileHandler',
+            'filename': os.path.join(LOG_DIR, 'project.log'),
+            'when': 'midnight',
+            'backupCount': 7,
+            'formatter': 'verbose',
+        },
path: forum/forum/settings.py
user: login: mehalyna
body: Consider splitting logs by module
For example:
```
'file_users': {
    'class': 'logging.FileHandler',
    'filename': os.path.join(LOG_DIR, 'users.log'),
    'formatter': 'verbose',
}
```
Then assign `users` logger to `file_users` instead of `file`. This isolates logs per module for large systems.
created_at: 2025-05-21T05:51:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -133,3 +133,53 @@
 # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+LOG_DIR = os.path.join(BASE_DIR, 'logs')
+if not os.path.exists(LOG_DIR):
+    os.makedirs(LOG_DIR)
path: forum/forum/settings.py
user: login: HlibSokol
body: added to newer version

created_at: 2025-05-22T14:06:05Z
author_association: COLLABORATOR
diff_hunk: @@ -133,3 +133,53 @@
 # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+LOG_DIR = os.path.join(BASE_DIR, 'logs')
+if not os.path.exists(LOG_DIR):
+    os.makedirs(LOG_DIR)
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '{asctime} [{levelname}] {name} → {message}',
+            'style': '{',
+        },
+        'simple': {
+            'format': '[{levelname}] {message}',
+            'style': '{',
+        },
+    },
+    'handlers': {
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'formatter': 'simple',
+        },
+        'file': {
+            'level': 'INFO',
+            'class': 'logging.handlers.TimedRotatingFileHandler',
+            'filename': os.path.join(LOG_DIR, 'project.log'),
+            'when': 'midnight',
+            'backupCount': 7,
+            'formatter': 'verbose',
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['console', 'file'],
+            'level': 'DEBUG',
+        },
+        'django.db.backends': {
+            'handlers': ['console', 'file'],
+            'level': 'INFO',
+            'propagate': False,
+        },
+        'users': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+        'profiles': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+        'projects': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+        'communications': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+        'dashboard': {'handlers': ['console', 'file'], 'level': 'DEBUG'},
+    },
path: forum/forum/settings.py
user: login: HlibSokol
body: also added, thanks

created_at: 2025-05-22T14:06:26Z
author_association: COLLABORATOR
diff_hunk: @@ -133,3 +133,53 @@
 # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
 
 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+LOG_DIR = os.path.join(BASE_DIR, 'logs')
+if not os.path.exists(LOG_DIR):
+    os.makedirs(LOG_DIR)
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '{asctime} [{levelname}] {name} → {message}',
+            'style': '{',
+        },
+        'simple': {
+            'format': '[{levelname}] {message}',
+            'style': '{',
+        },
+    },
+    'handlers': {
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'formatter': 'simple',
+        },
+        'file': {
+            'level': 'INFO',
+            'class': 'logging.handlers.TimedRotatingFileHandler',
+            'filename': os.path.join(LOG_DIR, 'project.log'),
+            'when': 'midnight',
+            'backupCount': 7,
+            'formatter': 'verbose',
+        },
path: forum/forum/settings.py
user: login: HlibSokol
body: good idea, I have split them 
created_at: 2025-05-22T14:18:36Z
author_association: COLLABORATOR

PR comments:
user: login: HlibSokol
created_at: 2025-05-22T14:39:58Z
body: @mehalyna I tried using logging.yaml - this is great thing, but for me it's better to leave logging configuration in settings.py. Regarding other comments - all moments have been applied, they will appear in next commit, thank you a lot for your hints!
author_association: COLLABORATOR
user: login: HlibSokol
created_at: 2025-05-22T14:56:22Z
body: - properly commented examples for views logging
- remade logging configuration in setting.py: added root section, split file section for different modules, changed process of creating directory for logs
- replaced logs/ directory in .gitignore for better experience 
- properly logged RegisterView in forum/users/views.py and deleted unnecessary example
- tested in Postman - logging works correctly
![image](https://github.com/user-attachments/assets/607d7d4a-04ad-4619-a6e8-321e80f6c3a1)
![image](https://github.com/user-attachments/assets/be8d025f-d498-4de7-912c-328badd0098b)

author_association: COLLABORATOR