Fonksiyon Deseni ile Hata Yakalama

Standard

Merhaba;

Şu an üzerinde çalıştığım projede (ve genel olarak) hataları yakalama ve düzenli olarak loglama ihtiyacı hissettim, elbette try-except blokları ile bu yapıyı sağlayabilsem de, bir yerden sonra kodun hemen her parçasını try-except yapısına boğduğumu ve bu yapının da kodun okunaklığına ve akışına zarar verdiğini gördüm. Biraz araştırma yapınca, bu kontrol sistemi için güzel bir yapının olduğunu gördüm. Öncelikle fonksiyon deseni hakkında genel bir bilgiye sahip olmak gerekli bu yapıyı kullanabilmek için, bunun için araştırdıklarım arasında en güzel anlatım şu kaynakta mevcuttu;

http://thecodeship.com/patterns/guide-to-python-function-decorators/

Özetle, fonksiyon deseni, aslında bildiğimiz dekoratör deseninin fonksiyonlara uyarlanmış versiyonudur. Yani dekoratör deseninde sınıfları implemente(Türkçesi saçma olduğu için mecburen ingilizcesini Türkçeleştirmişcesine kullandım kusura bakmayın) ederken, manipülasyon için kullanırken, fonksiyon deseninde de, direk fonksiyonları bir başka fonksiyondan üzerinden çağırarak kontrol ve manipülasyon işini bu fonksiyon üzerinden yapıyoruz. Bu sayede hata durumlarını burada filtreleyerek loglayabiliyoruz. Yapı olarak wrapper’lara benzemektedir. Python’u burada işlevsel kılan ise functool’dur. Functool altındaki wraps sayesinde, içerisine gönderdiğimiz fonksiyonun kimlik bilgilerini kaybetmeden, fonksiyon işlemlerini yapabilme yetisine sahip oluyoruz.

Hadi bunu biraz kod ile açıklayalım,

 # Author: Sezer Yavuzer Bozkir <admin@sezerbozkir.com>
# Created Date: 03.05.2017
# Website: sezerbozkir.com

from functools import wraps


def tags(tag_name):
    def tags_decorator(func):
        @wraps(func) # Fonksiyonun özelliklerini aktarmayı sağlayan wrapper
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, func(name)) # Fonksiyonun manipülasyonu

        return func_wrapper 

    return tags_decorator


@tags("p")
def get_text(name):
    """İçeriğe dair detaylar"""
    return "Hello " + name


print(get_text.__name__)  # get_text
print(get_text.__doc__)  # returns some text
print(get_text.__module__)  # __main__
print(get_text("ornek"))

Kodu çalıştırdığınızda, şöyle bir çıktı elde etmekteyiz;

 

 

 

 

Şimdi gelelim bunu hata yakalama işlevi için nasıl kullanacağımıza. Az-çok mantığını kestirebildiğinizi düşünüyorum. Yakaladığım hataları yazmak için logging kütüphanesinden faydalanıyorum (istek olursa bir gün onu da anlatırım pek tabii);

 # Author: Sezer Yavuzer Bozkir <admin@sezerbozkir.com>
# License: REIDIN Commercial
# Created Date: 03.05.2017
# Version:
# Website: sezerbozkir.com

import functools
import logging


def create_logger():
    """
    Logger oluşturup, döndurecek fonksiyon
    """
    logger = logging.getLogger("ornek_logger")
    logger.setLevel(logging.INFO)

    # Logun kaydedileceği dosyayı belirt, burada path de kullanılabilir orn: /var/log/ornek.log
    fh = logging.FileHandler("hata_kaydedici.log")

    fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    formatter = logging.Formatter(fmt)
    fh.setFormatter(formatter)

    # Hata yakalayıca formatı ekle
    logger.addHandler(fh)
    return logger


def exception(function):
    """
    A decorator that wraps the passed in function and logs 
    exceptions should one occur
    Olası Hataları yakalayıp loglayacak fonksiyon
    """

    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        logger = create_logger()
        try:
            return function(*args, **kwargs)
        except:
            # Hatayı logla
            err = "There was an exception in  "
            err += function.__name__
            logger.exception(err)

            # (isteğe bağlı) hatayı tekrar fırlat
            raise

    return wrapper

Artık hangi fonksiyonu bu filtreye dahil edecek isek, başına @exception eklememiz yeterli olacaktır.

Örnek kullanımı;

 # Author: Sezer Yavuzer Bozkir <admin@sezerbozkir.com>
# Created Date: 03.05.2017
# Website: sezerbozkir.com
from error_handler import exception


@exception
def sifira_bolunme_denemesi():
    1 / 0

if __name__ == '__main__':
    sifira_bolunme_denemesi()

Log kaydı;

 

 

 

 

Umarım faydalı olabilmişimdir.

Data detaylı bilgi için kaynak

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir