Python | Django | ログの実装方法

2021年9月2日

公開日:2021/9/2

Pythonには,DjangoというWebアプリケーションフレームワークがある.フレームワークのため,Djangoを利用するとWebアプリを通常よりも短時間で開発することが可能になる.

本記事では,「ログの実装方法」を以下9つの構成にて記す.

  1. “log"の出力
  2. “logger"のインスタンス化
  3. “logger"の設定方法(基本)
  4. “logger"の設定方法(カスタマイズ)
  5. “log"のローテーション
  6. “log"のローテーションの設定方法
  7. 作成したサイトでのlogの設定
  8. ミドルウェアを追加した処理
    (1) URLへのアクセスログ1
    (2) URLへのアクセスログ2
    (3) URLへのアクセス時間ログ
  9. 参照

◆実施環境

Python 3.8.8
Django 3.2.3
VS Code 1.59.0

■ログの実装方法

  1. “log"の出力

ログの出力には以下のようなログレベルが設定されている.数字が大きいほどエラーが深刻であることを示している.なお,Pythonではログレベルを以下のように定義している.

(1) DEBUG: 低レベルのシステム情報
(2) INFO: 一般的なシステム情報
(3) WARNING: 小さい問題が発生
(4) ERROR: 大きい問題が発生
(5) CRITICAL: 重大な問題が発生

以下にフォルダの設定からログ出力を行っていく.

新たにフォルダを開く.そのため,VS Codeの"File"をクリックし,"Open Folder"からフォルダを選ぶ.

“myloggingtest"という名のフォルダを作成したので,当該フォルダを選択する.選択後,以下赤枠のようにフォルダが出来上がる.

“myloggingtest"の下の黒スペースで右クリックをすると以下画面が出力するので,"New File"をクリックする.

“1_logging_test.py"という名のファイルを以下のように作成した.

“1_logging_test.py"を以下のように編集する.

import logging

logging.basicConfig(
    level=logging.WARNING, filename='example.log',
    filemode='w', 
    format='%(asctime)s-%(process)s-%(levelname)s-%(message)s'
)
logging.debug('debug.log')
logging.info('info.log')
logging.warning('warning.log')
logging.error('error.log')
logging.critical('critical.log')

コードを記述した画面で右クリックをすると以下画面が出力するので,"Run Python File in Terminal"をクリックする.

ターミナルに以下が出力される.

PS C:\Users\shiro\Desktop\210517_python development\myloggingtest> & C:/Users/shiro/anaconda3/envs/djangoenv/python.exe "c:/Users/shiro/Desktop/210517_python development/myloggingtest/1_logging_test.py"*/

加えて,"myloggingtest"に以下のように"example.log"が出力する.

“1_logging_test.py"の4行目に"WARNING"を利用したので,"example.log"の中は,以下コードが記載される.

2021-08-26 15:56:55,020-10312-WARNING-warning.log
2021-08-26 15:56:55,020-10312-ERROR-error.log
2021-08-26 15:56:55,020-10312-CRITICAL-critical.log

“1_logging_test.py"を以下のように変更し,"Run Python File in Terminal"を実行する.

import logging

logging.basicConfig( # 変更箇所
    level=logging.INFO, filename='example.log',
    filemode='w', 
    format='%(asctime)s-%(process)s-%(levelname)s-%(message)s'
)
logging.debug('debug.log')
logging.info('info.log')
logging.warning('warning.log')
logging.error('error.log')
logging.critical('critical.log')

“example.log"の中は,以下コードが記載される.

2021-08-26 16:08:15,612-8640-INFO-info.log
2021-08-26 16:08:15,612-8640-WARNING-warning.log
2021-08-26 16:08:15,612-8640-ERROR-error.log
2021-08-26 16:08:15,612-8640-CRITICAL-critical.log

“1_logging_test.py"に以下を追記し,編集し,"Run Python File in Terminal"を実行する.

import logging

logging.basicConfig(
    level=logging.INFO, filename='example.log',
    filemode='w', 
    format='%(asctime)s-%(process)s-%(levelname)s-%(message)s'
)
logging.debug('debug.log')
logging.info('info.log')
logging.warning('warning.log')
logging.error('error.log')
logging.critical('critical.log')

name = "XYZ's PC" # 追記箇所
logging.critical(f'- {name} has error')

“example.log"の中は,以下コードが記載される.

2021-08-26 16:31:27,191-732-INFO-info.log
2021-08-26 16:31:27,191-732-WARNING-warning.log
2021-08-26 16:31:27,191-732-ERROR-error.log
2021-08-26 16:31:27,191-732-CRITICAL-critical.log
2021-08-26 16:31:27,192-732-CRITICAL-- XYZ's PC has error

“1_logging_test.py"に以下を追記し,編集し,"Run Python File in Terminal"を実行する.

import logging

logging.basicConfig(
    level=logging.INFO, filename='example.log',
    filemode='w', 
    format='%(asctime)s-%(process)s-%(levelname)s-%(message)s'
)
logging.debug('debug.log')
logging.info('info.log')
logging.warning('warning.log')
logging.error('error.log')
logging.critical('critical.log')

name = "XYZ's PC"
logging.critical(f'{name} has error')

try: # 追記箇所
    result = 10/0
except Exception as e:
    logging.error(e, exc_info=True)

“example.log"の中は,以下コードが出力される."1_logging_test.py"にて"exc_info=True"を記載したことによって,エラーが19行目で発生したことがわかる.

2021-08-26 17:14:02,749-13804-INFO-info.log
2021-08-26 17:14:02,749-13804-WARNING-warning.log
2021-08-26 17:14:02,749-13804-ERROR-error.log
2021-08-26 17:14:02,749-13804-CRITICAL-critical.log
2021-08-26 17:14:02,750-13804-CRITICAL-- XYZ's PC has error
2021-08-26 17:14:02,750-13804-ERROR-division by zero
Traceback (most recent call last):
  File "c:/Users/shiro/Desktop/210517_python development/myloggingtest/1_logging_test.py", line 19, in 
    result = 10/0
ZeroDivisionError: division by zero
  1. “logger"のインスタンス化

“2_logging_test.py"という名のファイルを以下のように作成した.

“2_logging_test.py"を以下のように編集する.

import logging

logger = logging.getLogger(__name__)

# logger level
logger.setLevel(logging.DEBUG)

# handler
st_handler = logging.StreamHandler()
fl_handler = logging.FileHandler('example2.log', encoding='utf-8')

# handler log level
st_handler.setLevel(logging.WARNING)
fl_handler.setLevel(logging.ERROR)

# Formatter
st_formatter = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')
fl_formatter = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')

# setting formatter in handler
st_handler.setFormatter(st_formatter)
fl_handler.setFormatter(fl_formatter)

# setting handler in logger
logger.addHandler(st_handler)
logger.addHandler(fl_handler)

#
logger.debug('debug log')
logger.info('info log')
logger.warning('warning log')
logger.error('error log')
logger.critical('critical log')

上記を保存し,"Run Python File in Terminal"をクリックするとターミナルには以下が出力される."2_logging_test.py"に"st_handler.setLevel(logging.WARNING)"を記述したため,"WARNING"以上の内容となっている.

PS C:\Users\shiro\Desktop\210517_python development\myloggingtest
> & C:/Users/shiro/anaconda3/envs/djangoenv/python.exe 
"c:/Users/shiro/Desktop/210517_python development/myloggingtest/2_logging_test.py"

2021-08-30 14:45:34,818-__main__-WARNING-warning log # 以下出力箇所
2021-08-30 14:45:34,818-__main__-ERROR-error log
2021-08-30 14:45:34,819-__main__-CRITICAL-critical log

同時に"example2.log"ファイルが以下のように出力される.

“2_logging_test.py"に"fl_handler.setLevel(logging.ERROR)"を記述したため,"example2.log"には"ERROR"以上の内容となっている.

2021-08-30 14:49:58,857-__main__-ERROR-error log
2021-08-30 14:49:58,858-__main__-CRITICAL-critical log

“2_logging_test.py"に以下を追記し,"Run Python File in Terminal"を実行する.

import logging

logger = logging.getLogger(__name__)

# logger level
logger.setLevel(logging.DEBUG)

# handler
st_handler = logging.StreamHandler()
fl_handler = logging.FileHandler('example2.log', encoding='utf-8')

# handler log level
st_handler.setLevel(logging.WARNING)
fl_handler.setLevel(logging.ERROR)

# Formatter
st_formatter = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')
fl_formatter = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')

# setting formatter in handler
st_handler.setFormatter(st_formatter)
fl_handler.setFormatter(fl_formatter)

# setting handler in logger
logger.addHandler(st_handler)
logger.addHandler(fl_handler)

#
logger.debug('debug log')
logger.info('info log')
logger.warning('warning log')
logger.error('error log')
logger.critical('critical log')

try: # 追記箇所
    result = 10/0
except Exception as e:
    logger.critical(e, exc_info=True)

ターミナルには,以下が出力される.

PS C:\Users\shiro\Desktop\210517_python development\myloggingtest> 
    > & C:/Users/shiro/anaconda3/envs/djangoenv/python.exe 
"c:/Users/shiro/Desktop/210517_python development/myloggingtest/2_logging_test.py"

2021-08-30 15:21:14,767-__main__-WARNING-warning log # 以下出力箇所
2021-08-30 15:21:14,767-__main__-ERROR-error log
2021-08-30 15:21:14,768-__main__-CRITICAL-critical log
2021-08-30 15:21:14,768-__main__-CRITICAL-division by zero

Traceback (most recent call last):
  File "c:/Users/shiro/Desktop/210517_python development/myloggingtest/2_logging_test.py", line 37, in 
    result = 10/0
ZeroDivisionError: division by zero

加えて,"example2.log"の中には,以下コードが出力される.

2021-08-30 14:49:58,857-__main__-ERROR-error log
2021-08-30 14:49:58,858-__main__-CRITICAL-critical log
2021-08-30 15:09:23,993-__main__-ERROR-error log
2021-08-30 15:09:23,993-__main__-CRITICAL-critical log
2021-08-30 15:14:35,269-__main__-ERROR-error log
2021-08-30 15:14:35,269-__main__-CRITICAL-critical log
2021-08-30 15:21:14,767-__main__-ERROR-error log
2021-08-30 15:21:14,768-__main__-CRITICAL-critical log
2021-08-30 15:21:14,768-__main__-CRITICAL-division by zero
Traceback (most recent call last):
  File "c:/Users/shiro/Desktop/210517_python development/myloggingtest/2_logging_test.py", line 37, in 
    result = 10/0
ZeroDivisionError: division by zero
  1. “logger"の設定方法(基本)

“3_logging_test.py"という名のファイルを以下のように作成する.

“3_logging_test.py"を以下のように編集する.

import logging
import logging.config

logging.config.fileConfig(name='configtest/logger1.conf')

“3_logging_test.py"に"config/logger1.conf"を記述したので,"config"フォルダを作成し,その中に"logger1.conf"を作成する.

“logger1.conf"を以下のように編集する.なお,"[handler_filehandler]"の"args"の中の"w"は新規作成を意味する."a"に変えると追記となる.

[loggers]
keys=root

[handlers]
keys=consolehandler, filehandler

[formatters]
keys=sampleformatter

[logger_root]
level=DEBUG
handlers=consolehandler, filehandler

[handler_consolehandler]
class=StreamHandler
level=WARNING
formatter=sampleformatter
args=(sys.stdout,)

[handler_filehandler]
class=FileHandler
level=ERROR
formatter=sampleformatter
args=['logs/error.log', 'w', 'utf-8']

[formatter_sampleformatter]
format=%(asctime)s-%(name)s-%(message)s

“logger1.conf"で"logs/error.log"と記述したので,"logs"フォルダを以下のように作成する.

“3_logging_test.py"に追記し,"Run Python File in Terminal"を実行する.

import logging
import logging.config

logging.config.fileConfig(name='configtest/logger1.conf')

logger = logging.getLogger(__name__) # 以下追記箇所

logger.debug('debug log')
logger.info('info log')
logger.warning('warning log')
logger.error('error log')
logger.critical('critical log')

ターミナルには以下が出力される."configtest/logger1.conf"の"StreamHandler"が"Warning"のため,"warning"以上のログが出力されている.

PS C:\Users\shiro\Desktop\210517_python development\myloggingtest> 
& C:/Users/shiro/anaconda3/envs/djangoenv/python.exe 
"c:/Users/shiro/Desktop/210517_python development/myloggingtest/3_logging_test.py"

2021-08-31 14:55:55,739-__main__-warning log # 以下出力箇所
2021-08-31 14:55:55,740-__main__-error log
2021-08-31 14:55:55,741-__main__-critical log

加えて,"logs"フォルダには"error.log"が作成される.

“configtest/logger1.conf"の"FileHandler"が"Error"のため,"logs/error.log"には以下のように"error"以上が出力される.

2021-08-31 14:55:55,740-__main__-error log
2021-08-31 14:55:55,741-__main__-critical log
  1. “logger"の設定方法(カスタマイズ)

“configtest/logger1.conf"に追記し,以下のように編集する.以下を利用する際,#の注意書きはエラーの原因になるので,削除すること.

[loggers]
keys=root, examplelogger # 変更箇所

[handlers]
keys=consolehandler, filehandler, exampleconsolehandler # 変更箇所

[formatters]
keys=sampleformatter

[logger_root]
level=DEBUG
handlers=consolehandler, filehandler

[logger_examplelogger] # 追記箇所(14~18行目)
level=DEBUG
handlers=filehandler, exampleconsolehandler
qualname=examplelogger
propagate=0 # 1だと"logger_root"も呼び出される

[handler_consolehandler]
class=StreamHandler
level=WARNING
formatter=sampleformatter
args=(sys.stdout,)

[handler_filehandler]
class=FileHandler
level=ERROR
formatter=sampleformatter
args=['logs/error.log', 'w', 'utf-8']

[handler_exampleconsolehandler] # 追記箇所(32~36行目)
class=StreamHandler
level=INFO
formatter=sampleformatter
args=[sys.stdout,]

[formatter_sampleformatter]
format=%(asctime)s-%(name)s-%(message)s

“3_logging_test.py"に追記し,"Run Python File in Terminal"を実行する.

import logging
import logging.config

logging.config.fileConfig(fname='configtest/logger1.conf')

logger = logging.getLogger(__name__)

logger.debug('debug log')
logger.info('info log')
logger.warning('warning log')
logger.error('error log')
logger.critical('critical log')


logger = logging.getLogger('examplelogger') # 以下追記箇所

logger.debug('debug log')
logger.info('info log')
logger.warning('warning log')
logger.error('error log')
logger.critical('critical log')

ターミナルには以下が出力される.

PS C:\Users\shiro\Desktop\210517_python development\myloggingtest
> n 46:  & C:/Users/shiro/anaconda3/envs/djangoenv/python.exe 
"c:/Users/shriro/Desktop/210517_python development/myloggingtest/3_logging_testy& C:/U.py"                                                               top/21

2021-08-31 16:19:16,807-__main__-warning log # 以下出力箇所
2021-08-31 16:19:16,807-__main__-error log
2021-08-31 16:19:16,807-__main__-critical log
2021-08-31 16:19:16,808-examplelogger-info log
2021-08-31 16:19:16,809-examplelogger-warning log
2021-08-31 16:19:16,809-examplelogger-error log
2021-08-31 16:19:16,809-examplelogger-critical log
  1. “log"のローテーション

“4_logging_test.py"という名のファイルを以下のように作成する.

“4_logging_test.py"を以下のように編集する.その後,"Run Python File in Terminal"を実行する.

import logging
import logging.handlers

logger = logging.getLogger(__name__)

ro_handler = logging.handlers.RotatingFileHandler(
    'logs/rotation.log',
    maxBytes=1000,
    backupCount=6,
    encoding='utf-8',
)

ti_handler = logging.handlers.TimedRotatingFileHandler(
    'logs/time_rotation.log',
    when='S', # 15secごと
    interval=15,
    backupCount=6,
    encoding='utf-8',
)

logger.setLevel(logging.DEBUG)
example_formatter = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')
ro_handler.setFormatter(example_formatter)
ti_handler.setFormatter(example_formatter)
logger.addHandler(ro_handler)
logger.addHandler(ti_handler)

import time

for _ in range(500):
    logger.debug('debug log')
    logger.info('info log')
    logger.warning('warning log')
    logger.error('error log')
    logger.critical('critical log')
    time.sleep(1)

“logs"フォルダには以下のようにlogファイルが出力される.

  1. “log"のローテーションの設定方法

“5_logging_test.py"という名のファイルを以下のように作成する.

“5_logging_test.py"を以下のように編集する.

import logging
import logging.config

logging.config.fileConfig(fname='configtest/rot_logger1.conf')

“5_logging_test.py"で"configtest/rot_logger1.conf"を記述したので,"configtest"フォルダに"rot_logger1.conf"を以下のように作成する.

“rot_logger1.conf"を以下のように編集する.

[loggers]
keys=root

[handlers]
keys=rotatinghandler, timedrotatinghandler

[formatters]
keys=sampleformatter

[logger_root]
level=DEBUG
handlers=rotatinghandler, timedrotatinghandler

[handler_rotatinghandler]
class=handlers.RotatingFileHandler
level=INFO
formatter=sampleformatter
args=('logs/custom_rotation.log', 'a', 1000, 5, 'utf-8')

[handler_timedrotatinghandler]
class=handlers.TimedRotatingFileHandler
level=WARNING
formatter=sampleformatter
args=('logs/custom_t_rotation.log', 'S', 5, 6, 'utf-8')

[formatter_sampleformatter]
format=%(asctime)s-%(name)s-%(levelname)s-%(message)s

“5_logging_test.py"を以下のように編集する.その後,"Run Python File in Terminal"を実行する.

import logging
import logging.config

logging.config.fileConfig(fname='configtest/rot_logger1.conf')
logger = logging.getLogger(__name__)

import time
for _ in range(1000):
    logger.debug('debug log')
    logger.info('info log')
    logger.warning('warning log')
    logger.error('error log')
    logger.critical('critical log')
    time.sleep(1)

“logs"フォルダには以下のように"custom_rotation.log"と"custom_t_rotation.log"が出力された.

  1. 作成したサイトでの"log"の設定

8/16~8/23に作成したプロジェクトを利用してlogの設定をする.以下は8/16と8/23に作成した記事のURLとなる.(*当該期間に以下2つの記事以外にも作成している)

8/16作成:class-based viewsの基本的な実装方法
8/23作成:class-based viewsにおけるSuccessMessageMixinの使い方

VS Codeの"File"をクリックし,"Open Folder"をクリックする.

“myclassbasedviewtest"フォルダをクリックする.クリックすると以下のように作成したプロジェクトが出現する.

“class_based_view/settings.py"の最下部に追記し,以下のように編集する.なお,"application-logger"の"level"は,"console_handler"の"level"よりも低くすることはできない.

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGGING = { # 以下追記箇所
    'version': 1,
    'disable_existing_loggers': False,
    'formatters':{
        'simple':{
            'format': '%(asctime)s %(levelname)s [%(pathname)s:%(lineno)s] %(message)s',
        }
    },
    'handlers':{
        'console_handler':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
            'formatter':'simple',
        }
    },
    'loggers':{
        'application-logger':{
            'handlers':['console_handler',],
            'level':'DEBUG',
            'propagate':False,
        }
    }
}

“ec_site/views.py"に追記し,以下のように編集する.

from django.shortcuts import render
from django.views.generic.base import (
    View, TemplateView, RedirectView,
)
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from django.views.generic.edit import (
    CreateView, UpdateView, DeleteView,
    FormView,
)
from . import forms
import random
from .models import Medicines
from datetime import datetime
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
import logging # 追記箇所

application_logger = logging.getLogger('application-logger') # 追記箇所

class TopView(View):

    def get(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

    def post(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm(request.POST or None)
        if medicine_form.is_valid():
            medicine_form.save()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

class HomeView(TemplateView):

    template_name = 'home.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        application_logger.debug('log action in Home page!') # 追記箇所
        context['name'] = kwargs.get('name')
        context['alphabet'] = random.randint(0,9999)
        return context

class MedicineDetailView(DetailView):
    model = Medicines
    template_name = 'medicine.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['time'] = datetime.now()
        return context

class MedicineListView(ListView):
    model = Medicines
    template_name = 'medicine_list.html'

    def get_queryset(self):
        qs = super(MedicineListView, self).get_queryset()
        if 'name' in self.kwargs:
            qs = qs.filter(name__startswith=self.kwargs['name'])
        qs = qs.order_by('-id')
        return qs

class MedicineCreateView(CreateView):
    model = Medicines
    fields = ['name', 'description', 'price']
    template_name = 'insert_medicine.html'

    def form_valid(self, form):
        form.instance.create_time = datetime.now()
        form.instance.update_time = datetime.now()
        return super(MedicineCreateView, self).form_valid(form)

    def get_initial(self, **kwargs):
        initial = super(MedicineCreateView, self).get_initial(**kwargs)
        initial['price'] = '1000'
        return initial

class MedicineUpdateView(SuccessMessageMixin, UpdateView):
    template_name = 'update_medicine.html'
    model = Medicines
    form_class = forms.MedicineUpdateForm
    success_message = 'Update is successful!'

    def get_success_url(self):
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': self.object.id})

    def get_success_message(self, cleaned_data):
        return cleaned_data.get('name') + ' was updated'

class MedicineDeleteView(DeleteView):
    model = Medicines
    template_name = 'delete_medicine.html'
    success_url = reverse_lazy('ec_site:list_medicines')

class MedicineFormView(FormView):

    template_name = 'form_medicine.html'
    form_class = forms.MedicineForm
    success_url = reverse_lazy('ec_site:list_medicines')

    def get_initial(self):
        initial = super(MedicineFormView, self).get_initial()
        initial['price'] = '2000'
        return initial

    def form_valid(self, form):
        if form.is_valid():
            form.save()
        return super(MedicineFormView, self).form_valid(form)

class MedicineRedirectView(RedirectView):
    url = 'https://shelokuma.com'

    def get_redirect_url(self, *args, **kwargs):
        medicine = Medicines.objects.last()
        if 'pk' in kwargs:
            return reverse_lazy('ec_site:detail_medicine', kwargs={'pk':kwargs['pk']})
        
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': medicine.pk})

VS Codeの"View"を開き,"Terminal"をクリックする.VS Codeの下部にターミナルが開くので,”conda activate 仮想環境名”を実行し,仮想環境に移行する(移行方法の詳細はこちら).ターミナルで"cd class_based_view"(プロジェクト名)を入力し,ディレクトリを変更する.変更後,以下のように”python manage.py runserver”を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
September 01, 2021 - 16:26:29
Django version 3.2.3, using settings 'class_based_view.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

上記の"http://127.0.0.1:8000/"をクリックすると,ブラウザが開き,以下画面が出力される.

ブラウザのURLに"http://http://127.0.0.1:8000/ec_site/home/xyz"を入力すると以下画面に遷移する.

同時にターミナルには以下が出力される.

2021-09-01 16:29:34,394 DEBUG 
[C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\ec_site\views.py:43] 
log action in Home page!
[01/Sep/2021 16:29:34] "GET /ec_site/home/xyz HTTP/1.1" 200 82

“class_based_view/settings.py"の最下部に追記し,以下のように編集する.

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters':{
        'simple':{
            'format': '%(asctime)s %(levelname)s [%(pathname)s:%(lineno)s] %(message)s',
        }
    },
    'handlers':{
        'console_handler':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
            'formatter':'simple',
        },
        'timed_file_handler':{ # 追記箇所(143~152行目)
            'level':'INFO',
            'class':'logging.handlers.TimedRotatingFileHandler',
            'filename':os.path.join('logs','application.log'),
            'when':'S',
            'backupCount':10,
            'formatter':'simple',
            'encoding':'utf-8',
            'delay':True,
        }
    },
    'loggers':{
        'application-logger':{
            'handlers':['console_handler', 'timed_file_handler'], # 変更箇所
            'level':'DEBUG',
            'propagate':False,
        }
    }
}

“ec_site/views.py"に追記し,以下のように編集する.

from django.shortcuts import render
from django.views.generic.base import (
    View, TemplateView, RedirectView,
)
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from django.views.generic.edit import (
    CreateView, UpdateView, DeleteView,
    FormView,
)
from . import forms
import random
from .models import Medicines
from datetime import datetime
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
import logging

application_logger = logging.getLogger('application-logger')

class TopView(View):

    def get(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

    def post(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm(request.POST or None)
        if medicine_form.is_valid():
            medicine_form.save()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

class HomeView(TemplateView):

    template_name = 'home.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        application_logger.debug('log action in Home page!')
        context['name'] = kwargs.get('name')
        context['alphabet'] = random.randint(0,9999)
        return context

class MedicineDetailView(DetailView):
    model = Medicines
    template_name = 'medicine.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['time'] = datetime.now()
        return context

class MedicineListView(ListView):
    model = Medicines
    template_name = 'medicine_list.html'

    def get_queryset(self):
        qs = super(MedicineListView, self).get_queryset()
        if 'name' in self.kwargs:
            qs = qs.filter(name__startswith=self.kwargs['name'])
        qs = qs.order_by('-id')
        return qs

class MedicineCreateView(CreateView):
    model = Medicines
    fields = ['name', 'description', 'price']
    template_name = 'insert_medicine.html'

    def form_valid(self, form):
        form.instance.create_time = datetime.now()
        form.instance.update_time = datetime.now()
        return super(MedicineCreateView, self).form_valid(form)

    def get_initial(self, **kwargs):
        initial = super(MedicineCreateView, self).get_initial(**kwargs)
        initial['price'] = '1000'
        return initial

class MedicineUpdateView(SuccessMessageMixin, UpdateView):
    template_name = 'update_medicine.html'
    model = Medicines
    form_class = forms.MedicineUpdateForm
    success_message = 'Update is successful!'

    def get_success_url(self):
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': self.object.id})

    def get_success_message(self, cleaned_data):
        application_logger.warning('update medicine!') # 追記箇所
        return cleaned_data.get('name') + ' was updated'

class MedicineDeleteView(DeleteView):
    model = Medicines
    template_name = 'delete_medicine.html'
    success_url = reverse_lazy('ec_site:list_medicines')

class MedicineFormView(FormView):

    template_name = 'form_medicine.html'
    form_class = forms.MedicineForm
    success_url = reverse_lazy('ec_site:list_medicines')

    def get_initial(self):
        initial = super(MedicineFormView, self).get_initial()
        initial['price'] = '2000'
        return initial

    def form_valid(self, form):
        if form.is_valid():
            form.save()
        return super(MedicineFormView, self).form_valid(form)

class MedicineRedirectView(RedirectView):
    url = 'https://shelokuma.com'

    def get_redirect_url(self, *args, **kwargs):
        medicine = Medicines.objects.last()
        if 'pk' in kwargs:
            return reverse_lazy('ec_site:detail_medicine', kwargs={'pk':kwargs['pk']})
        
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': medicine.pk})

“class_based_view/settings.py"に"logs"フォルダについて記述したので,"class_based_view"プロジェクトフォルダに以下のように"logs"フォルダを作成する.

上記を保存し,ターミナルを開き,仮想環境に移行し,"cd class_based_view"ディレクトリに移行する.その後,以下のように”python manage.py runserver”を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
September 02, 2021 - 15:07:41
Django version 3.2.3, using settings 'class_based_view.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

上記の"http://127.0.0.1:8000/"をクリックすると,ブラウザが開き,以下画面が出力される.

ブラウザのURLに"http://127.0.0.1:8000/ec_site/home/jotaro"を記入すると以下画面に遷移する.

同時にターミナルには以下が出力される.なお,"logs"フォルダにlogが出力されるのは"INFO"以上なので,今回の情報は"logs"フォルダに出力されない.

2021-09-02 15:26:00,390 DEBUG 
[C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\ec_site\views.py:43] 
log action in Home page!

[02/Sep/2021 15:26:00] "GET /ec_site/home/jotaro HTTP/1.1" 200 85

ブラウザのURLに"http://127.0.0.1:8000/ec_site/list_medicines"を記入すると以下画面に遷移する.

ブラウザのURLに"http://127.0.0.1:8000/ec_site/update_medicine/1″を記入すると以下画面に遷移する.

NameとPriceを以下のように変更し,"update"ボタンをクリックすると,上部にメッセージが出現する.

ブラウザのURLに"http://127.0.0.1:8000/ec_site/list_medicines"に戻ると以下のように内容が変更されている.

ターミナルには,以下のようにログが出力される.

2021-09-02 15:37:19,267 WARNING 
[C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\ec_site\views.py:94] 
update medicine!

“logs"フォルダには"application.log"が作成され,以下内容が記述される.

2021-09-02 15:37:19,267 WARNING 
[C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\ec_site\views.py:94] 
update medicine!

“ec_site/views.py"に追記し,以下のように編集する.

from django.shortcuts import render
from django.views.generic.base import (
    View, TemplateView, RedirectView,
)
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from django.views.generic.edit import (
    CreateView, UpdateView, DeleteView,
    FormView,
)
from . import forms
import random
from .models import Medicines
from datetime import datetime
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
import logging
from django.http import Http404 # 追記箇所

application_logger = logging.getLogger('application-logger')

class TopView(View):

    def get(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

    def post(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm(request.POST or None)
        if medicine_form.is_valid():
            medicine_form.save()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

class HomeView(TemplateView):

    template_name = 'home.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        application_logger.debug('log action in Home page!')
        if kwargs.get('name') == 'bad': # 追記箇所(45~46行目)
            raise Http404("You can't use this name")
        context['name'] = kwargs.get('name')
        context['alphabet'] = random.randint(0,9999)
        return context

class MedicineDetailView(DetailView):
    model = Medicines
    template_name = 'medicine.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['time'] = datetime.now()
        return context

class MedicineListView(ListView):
    model = Medicines
    template_name = 'medicine_list.html'

    def get_queryset(self):
        qs = super(MedicineListView, self).get_queryset()
        if 'name' in self.kwargs:
            qs = qs.filter(name__startswith=self.kwargs['name'])
        qs = qs.order_by('-id')
        return qs

class MedicineCreateView(CreateView):
    model = Medicines
    fields = ['name', 'description', 'price']
    template_name = 'insert_medicine.html'

    def form_valid(self, form):
        form.instance.create_time = datetime.now()
        form.instance.update_time = datetime.now()
        return super(MedicineCreateView, self).form_valid(form)

    def get_initial(self, **kwargs):
        initial = super(MedicineCreateView, self).get_initial(**kwargs)
        initial['price'] = '1000'
        return initial

class MedicineUpdateView(SuccessMessageMixin, UpdateView):
    template_name = 'update_medicine.html'
    model = Medicines
    form_class = forms.MedicineUpdateForm
    success_message = 'Update is successful!'

    def get_success_url(self):
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': self.object.id})

    def get_success_message(self, cleaned_data):
        application_logger.warning('update medicine!')
        return cleaned_data.get('name') + ' was updated'

class MedicineDeleteView(DeleteView):
    model = Medicines
    template_name = 'delete_medicine.html'
    success_url = reverse_lazy('ec_site:list_medicines')

class MedicineFormView(FormView):

    template_name = 'form_medicine.html'
    form_class = forms.MedicineForm
    success_url = reverse_lazy('ec_site:list_medicines')

    def get_initial(self):
        initial = super(MedicineFormView, self).get_initial()
        initial['price'] = '2000'
        return initial

    def form_valid(self, form):
        if form.is_valid():
            form.save()
        return super(MedicineFormView, self).form_valid(form)

class MedicineRedirectView(RedirectView):
    url = 'https://shelokuma.com'

    def get_redirect_url(self, *args, **kwargs):
        medicine = Medicines.objects.last()
        if 'pk' in kwargs:
            return reverse_lazy('ec_site:detail_medicine', kwargs={'pk':kwargs['pk']})
        
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': medicine.pk})

“class_based_view/settings.py"の最下部に追記し,以下のように編集する.

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters':{
        'simple':{
            'format': '%(asctime)s %(levelname)s [%(pathname)s:%(lineno)s] %(message)s',
        }
    },
    'handlers':{
        'console_handler':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
            'formatter':'simple',
        },
        'timed_file_handler':{
            'level':'INFO',
            'class':'logging.handlers.TimedRotatingFileHandler',
            'filename':os.path.join('logs','application.log'),
            'when':'S',
            'backupCount':10,
            'formatter':'simple',
            'encoding':'utf-8',
            'delay':True,
        }, # 追記箇所(152~161行目)
        'timed_warning_handler':{
            'level':'WARNING',
            'class':'logging.handlers.TimedRotatingFileHandler',
            'filename':os.path.join('logs','application_warning.log'),
            'when':'S',
            'backupCount':10,
            'formatter':'simple',
            'encoding':'utf-8',
            'delay':True,
        }
    },
    'loggers':{
        'application-logger':{
            'handlers':['console_handler', 'timed_file_handler'],
            'level':'DEBUG',
            'propagate':False,
        }, # 追記箇所(169~173行目)
        'warning-logger':{
            'handlers':['timed_warning_handler'],
            'level':'WARNING',
            'propagate':False,
        }
    }
}

“ec_site/views.py"に追記し,以下のように編集する.

from django.shortcuts import render
from django.views.generic.base import (
    View, TemplateView, RedirectView,
)
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from django.views.generic.edit import (
    CreateView, UpdateView, DeleteView,
    FormView,
)
from . import forms
import random
from .models import Medicines
from datetime import datetime
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
import logging
from django.http import Http404

application_logger = logging.getLogger('application-logger')
warning_logger = logging.getLogger('warning-logger') # 追記箇所

class TopView(View):

    def get(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

    def post(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm(request.POST or None)
        if medicine_form.is_valid():
            medicine_form.save()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

class HomeView(TemplateView):

    template_name = 'home.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        application_logger.debug('log action in Home page!')
        if kwargs.get('name') == 'bad': # 追記箇所(46~48行目)
            warning_logger.warning("You can't use 'bad'!")
            raise Http404("You can't use this 'bad'!")
        context['name'] = kwargs.get('name')
        context['alphabet'] = random.randint(0,9999)
        return context

class MedicineDetailView(DetailView):
    model = Medicines
    template_name = 'medicine.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['time'] = datetime.now()
        return context

class MedicineListView(ListView):
    model = Medicines
    template_name = 'medicine_list.html'

    def get_queryset(self):
        qs = super(MedicineListView, self).get_queryset()
        if 'name' in self.kwargs:
            qs = qs.filter(name__startswith=self.kwargs['name'])
        qs = qs.order_by('-id')
        return qs

class MedicineCreateView(CreateView):
    model = Medicines
    fields = ['name', 'description', 'price']
    template_name = 'insert_medicine.html'

    def form_valid(self, form):
        form.instance.create_time = datetime.now()
        form.instance.update_time = datetime.now()
        return super(MedicineCreateView, self).form_valid(form)

    def get_initial(self, **kwargs):
        initial = super(MedicineCreateView, self).get_initial(**kwargs)
        initial['price'] = '1000'
        return initial

class MedicineUpdateView(SuccessMessageMixin, UpdateView):
    template_name = 'update_medicine.html'
    model = Medicines
    form_class = forms.MedicineUpdateForm
    success_message = 'Update is successful!'

    def get_success_url(self):
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': self.object.id})

    def get_success_message(self, cleaned_data):
        application_logger.warning('update medicine!')
        return cleaned_data.get('name') + ' was updated'

class MedicineDeleteView(DeleteView):
    model = Medicines
    template_name = 'delete_medicine.html'
    success_url = reverse_lazy('ec_site:list_medicines')

class MedicineFormView(FormView):

    template_name = 'form_medicine.html'
    form_class = forms.MedicineForm
    success_url = reverse_lazy('ec_site:list_medicines')

    def get_initial(self):
        initial = super(MedicineFormView, self).get_initial()
        initial['price'] = '2000'
        return initial

    def form_valid(self, form):
        if form.is_valid():
            form.save()
        return super(MedicineFormView, self).form_valid(form)

class MedicineRedirectView(RedirectView):
    url = 'https://shelokuma.com'

    def get_redirect_url(self, *args, **kwargs):
        medicine = Medicines.objects.last()
        if 'pk' in kwargs:
            return reverse_lazy('ec_site:detail_medicine', kwargs={'pk':kwargs['pk']})
        
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': medicine.pk})

VS Codeのターミナルを開き,仮想環境に移行する.ターミナルで"cd class_based_view"のディレクトリに移行し,以下のように”python manage.py runserver”を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
September 02, 2021 - 16:43:17
Django version 3.2.3, using settings 'class_based_view.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

上記の"http://127.0.0.1:8000/"をクリックすると,ブラウザが開く.ブラウザのURLに"http://127.0.0.1:8000/ec_site/home/bad"を入力すると以下画面に遷移する."http://127.0.0.1:8000/ec_site/home/"の後ろに"bad"を加えたためである.

“logs"フォルダには以下のように"application_warning.log"が作成される.

“logs/application_warning.log"には以下のようにlogが出力される.

2021-09-02 16:49:14,712 WARNING 
[C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\ec_site\views.py:47] 
You can't use 'bad'!
  1. ミドルウェアを追加した処理

(1) URLへのアクセスログ1

“class_based_view"フォルダに"middleware.py"を以下のように作成する.

“class_based_view/middleware.py"を以下のように編集する.

import logging
from django.utils.deprecation import MiddlewareMixin

application_logger = logging.getLogger('application-logger')

class MiddlewareTest(MiddlewareMixin):

    def process_view(self, request, view_func, view_args, view_kwargs):
        application_logger.info(request.get_full_path())

“class_based_view/settings.py"の"MIDDLEWARE"に以下のように編集をする.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'ec_site',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'class_based_view.middleware.MiddlewareTest', # 追記箇所
]

VS Codeのターミナルを開き,仮想環境に移行する.ターミナルで"cd class_based_view"のディレクトリに移行し,以下のように”python manage.py runserver”を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
September 02, 2021 - 18:09:56
Django version 3.2.3, using settings 'class_based_view.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

上記の"http://127.0.0.1:8000/"をクリックすると,ブラウザが開く.ブラウザのURLに"http://127.0.0.1:8000/ec_site/list_medicines"を入力すると以下画面に遷移する.

ターミナルには以下が出力される.

2021-09-02 18:10:37,668 INFO 
[C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\class_based_view\middleware.py:9] 
/ec_site/list_medicines

同時に,"logs/application.log"には以下ログが出力される.

2021-09-02 18:10:37,668 INFO 
[C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\class_based_view\middleware.py:9] 
/ec_site/list_medicines

(2) URLへのアクセスログ2

“class_based_view/middleware.py"に追記し,以下のように編集する.

import logging
from django.utils.deprecation import MiddlewareMixin

application_logger = logging.getLogger('application-logger')
warning_logger = logging.getLogger('warning-logger') # 追記箇所

class MiddlewareTest(MiddlewareMixin):

    def process_view(self, request, view_func, view_args, view_kwargs):
        application_logger.info(request.get_full_path())

    def process_exception(self, request, exception): # 追記箇所(12~13行目)
        warning_logger.warning(exception, exc_info=True)

“ec_site/views.py"を以下のように編集する.

from django.shortcuts import render
from django.views.generic.base import (
    View, TemplateView, RedirectView,
)
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from django.views.generic.edit import (
    CreateView, UpdateView, DeleteView,
    FormView,
)
from . import forms
import random
from .models import Medicines
from datetime import datetime
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
import logging
from django.http import Http404

application_logger = logging.getLogger('application-logger')
warning_logger = logging.getLogger('warning-logger')

class TopView(View):

    def get(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

    def post(self, request, *args, **kwargs):
        medicine_form = forms.MedicineForm(request.POST or None)
        if medicine_form.is_valid():
            medicine_form.save()
        return render(request, 'top.html', context={
            'medicine_form': medicine_form,
        })

class HomeView(TemplateView):

    template_name = 'home.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        application_logger.debug('log action in Home page!')
        if kwargs.get('name') == 'bad': # 変更箇所(47行目を削除)
            raise Http404("You can't use this 'bad'!")
        context['name'] = kwargs.get('name')
        context['alphabet'] = random.randint(0,9999)
        return context

class MedicineDetailView(DetailView):
    model = Medicines
    template_name = 'medicine.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['time'] = datetime.now()
        return context

class MedicineListView(ListView):
    model = Medicines
    template_name = 'medicine_list.html'

    def get_queryset(self):
        qs = super(MedicineListView, self).get_queryset()
        if 'name' in self.kwargs:
            qs = qs.filter(name__startswith=self.kwargs['name'])
        qs = qs.order_by('-id')
        return qs

class MedicineCreateView(CreateView):
    model = Medicines
    fields = ['name', 'description', 'price']
    template_name = 'insert_medicine.html'

    def form_valid(self, form):
        form.instance.create_time = datetime.now()
        form.instance.update_time = datetime.now()
        return super(MedicineCreateView, self).form_valid(form)

    def get_initial(self, **kwargs):
        initial = super(MedicineCreateView, self).get_initial(**kwargs)
        initial['price'] = '1000'
        return initial

class MedicineUpdateView(SuccessMessageMixin, UpdateView):
    template_name = 'update_medicine.html'
    model = Medicines
    form_class = forms.MedicineUpdateForm
    success_message = 'Update is successful!'

    def get_success_url(self):
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': self.object.id})

    def get_success_message(self, cleaned_data):
        application_logger.warning('update medicine!')
        return cleaned_data.get('name') + ' was updated'

class MedicineDeleteView(DeleteView):
    model = Medicines
    template_name = 'delete_medicine.html'
    success_url = reverse_lazy('ec_site:list_medicines')

class MedicineFormView(FormView):

    template_name = 'form_medicine.html'
    form_class = forms.MedicineForm
    success_url = reverse_lazy('ec_site:list_medicines')

    def get_initial(self):
        initial = super(MedicineFormView, self).get_initial()
        initial['price'] = '2000'
        return initial

    def form_valid(self, form):
        if form.is_valid():
            form.save()
        return super(MedicineFormView, self).form_valid(form)

class MedicineRedirectView(RedirectView):
    url = 'https://shelokuma.com'

    def get_redirect_url(self, *args, **kwargs):
        medicine = Medicines.objects.last()
        if 'pk' in kwargs:
            return reverse_lazy('ec_site:detail_medicine', kwargs={'pk':kwargs['pk']})
        
        return reverse_lazy('ec_site:update_medicine', kwargs={'pk': medicine.pk})

VS Codeのターミナルを開き,仮想環境に移行する.ターミナルで"cd class_based_view"のディレクトリに移行し,以下のように”python manage.py runserver”を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
September 02, 2021 - 18:30:25
Django version 3.2.3, using settings 'class_based_view.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

上記の"http://127.0.0.1:8000/"をクリックすると,ブラウザが開く.ブラウザのURLに"http://127.0.0.1:8000/ec_site/home/"の後ろに"bad"を付けて入力すると以下画面に遷移する.

“class_based_view/logs/application_warning.log"には以下のようなログが出力される.

2021-09-02 18:38:16,096 WARNING [C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\class_based_view\middleware.py:13] You can't use this 'bad'!
Traceback (most recent call last):
  File "C:\Users\shiro\anaconda3\envs\djangoenv\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\shiro\anaconda3\envs\djangoenv\lib\site-packages\django\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\shiro\anaconda3\envs\djangoenv\lib\site-packages\django\views\generic\base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "C:\Users\shiro\anaconda3\envs\djangoenv\lib\site-packages\django\views\generic\base.py", line 159, in get
    context = self.get_context_data(**kwargs)
  File "C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\ec_site\views.py", line 48, in get_context_data
    raise Http404("You can't use this 'bad'!")
django.http.response.Http404: You can't use this 'bad'!

(3) URLへのアクセス時間ログ

“class_based_view/settings.py"の"LOGGING"を以下のように編集する.

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters':{
        'simple':{
            'format': '%(asctime)s %(levelname)s [%(pathname)s:%(lineno)s] %(message)s',
        }
    },
    'handlers':{
        'console_handler':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
            'formatter':'simple',
        },
        'timed_file_handler':{
            'level':'INFO',
            'class':'logging.handlers.TimedRotatingFileHandler',
            'filename':os.path.join('logs','application.log'),
            'when':'S',
            'backupCount':10,
            'formatter':'simple',
            'encoding':'utf-8',
            'delay':True,
        }, # 追記箇所(153~162行目)
        'timed_info_handler':{
            'level':'INFO',
            'class':'logging.handlers.TimedRotatingFileHandler',
            'filename':os.path.join('logs','application_info.log'),
            'when':'S',
            'backupCount':10,
            'formatter':'simple',
            'encoding':'utf-8',
            'delay':True,
        },
        'timed_warning_handler':{
            'level':'WARNING',
            'class':'logging.handlers.TimedRotatingFileHandler',
            'filename':os.path.join('logs','application_warning.log'),
            'when':'S',
            'backupCount':10,
            'formatter':'simple',
            'encoding':'utf-8',
            'delay':True,
        }
    },
    'loggers':{
        'application-logger':{
            'handlers':['console_handler', 'timed_file_handler'],
            'level':'DEBUG',
            'propagate':False,
        },
        'warning-logger':{
            'handlers':['timed_warning_handler'],
            'level':'WARNING',
            'propagate':False,
        }, # 追記箇所(186~190行目)
        'info-logger':{
            'handlers':['timed_info_handler'],
            'level':'INFO',
            'propagate':False,
        }
    }
}

“class_based_view/middleware.py"に追記し,以下のように編集する.

import logging
from django.utils.deprecation import MiddlewareMixin
import time # 追記箇所

application_logger = logging.getLogger('application-logger')
warning_logger = logging.getLogger('warning-logger')
info_logger = logging.getLogger('info-logger') # 追記箇所

class MiddlewareTest(MiddlewareMixin):

    def process_view(self, request, view_func, view_args, view_kwargs):
        application_logger.info(request.get_full_path())

    def process_exception(self, request, exception):
        warning_logger.warning(exception, exc_info=True)

class InfoMiddleware(MiddlewareMixin): # 以下追記箇所

    def process_view(self, request, view_func, view_args, view_kwargs):
        start_time = time.time()
        request.start_time = start_time

    def process_template_response(self, request, response):
        response_time = time.time() - request.start_time
        info_logger.info(f'{request.get_full_path()}: {response_time}sec!')
        return response

“class_based_view/settings.py"の"MIDDLEWARE"に追記し,以下のように編集する.時間を計測するので,"MIDDLEWARE"の一番上に追記するのがベストとなる.

MIDDLEWARE = [
    'class_based_view.middleware.InfoMiddleware', # 追記箇所
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'class_based_view.middleware.MiddlewareTest',
]

VS Codeのターミナルを開き,仮想環境に移行する.ターミナルで"cd class_based_view"のディレクトリに移行し,以下のように”python manage.py runserver”を実行する.以下が出力される.

(djangoenv) C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view
>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced). # 以下出力箇所
September 02, 2021 - 19:05:45
Django version 3.2.3, using settings 'class_based_view.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

上記の"http://127.0.0.1:8000/"をクリックすると,ブラウザが開く.ブラウザのURLに"http://127.0.0.1:8000/ec_site/list_medicines"を入力すると以下画面に遷移する.

“class_based_view/logs"には"application_ingo.log"が以下のように作成される.

“class_based_view/logs/application_ingo.log"には以下が出力される."http://127.0.0.1:8000/ec_site/list_medicines"を表示させるのに,"0.004988193511962891″秒かかることがわかる.

2021-09-02 19:08:12,174 INFO 
[C:\Users\shiro\Desktop\210517_python development\myclassbasedviewtest\class_based_view\class_based_view\middleware.py:25] 
/ec_site/list_medicines: 0.004988193511962891sec!
  1. 参照

https://docs.djangoproject.com/en/3.2/topics/logging/

https://docs.python.org/ja/3/howto/logging.html#logging-to-a-file

https://docs.python.org/ja/3/howto/logging.html#formatters

https://docs.djangoproject.com/ja/3.1/topics/logging/

https://docs.djangoproject.com/ja/3.1/topics/http/middleware/

以上

PythonDjango,ログ

Posted by クマガイ