Thiết lập thanh lịch của đăng nhập Python trong Django


101

Tôi vẫn chưa tìm ra cách thiết lập ghi nhật ký Python với Django mà tôi hài lòng. Yêu cầu của tôi khá đơn giản:

  • Các trình xử lý nhật ký khác nhau cho các sự kiện khác nhau - nghĩa là tôi muốn có thể đăng nhập vào các tệp khác nhau
  • Dễ dàng truy cập đăng nhập trong các mô-đun của tôi. Mô-đun sẽ có thể tìm thấy trình ghi nhật ký của nó mà không tốn nhiều công sức.
  • Nên dễ dàng áp dụng cho các mô-đun dòng lệnh. Các phần của hệ thống là các quy trình dòng lệnh hoặc daemon độc lập. Việc ghi nhật ký phải dễ dàng sử dụng với các mô-đun này.

Thiết lập hiện tại của tôi là sử dụng logging.conftệp và thiết lập đăng nhập vào từng mô-đun mà tôi đăng nhập. Nó cảm thấy không ổn.

Bạn có thiết lập ghi nhật ký mà bạn thích không? Vui lòng nêu chi tiết: bạn thiết lập cấu hình như thế nào (bạn sử dụng logging.confhay thiết lập nó trong mã), bạn khởi chạy trình ghi nhật ký ở đâu / khi nào và làm cách nào để bạn có quyền truy cập vào chúng trong các mô-đun của mình, v.v.


1
Bạn có thể thấy video màn hình sau hữu ích - ericholscher.com/blog/2008/aug/29/… . Ngoài ra, hỗ trợ tốt hơn cho việc đăng nhập Django đã được Simon Willison đề xuất (xem simonwillison.net/2009/Sep/28/ponies ).
Dominic Rodger

@Dominic Rodger - Bạn đã có thể ghi nhật ký linh hoạt các ứng dụng trong Django, đề xuất của Simon chủ yếu để tạo điều kiện đăng nhập nội bộ Django. Có một công việc đang diễn ra trong Python để thêm cấu hình dựa trên từ điển vào ghi nhật ký Python, từ đó Django có thể được hưởng lợi.
Vinay Sajip 21/10/09

Câu trả lời:


57

Cách tốt nhất mà tôi đã tìm thấy cho đến nay là khởi tạo thiết lập ghi nhật ký trong settings.py - không nơi nào khác. Bạn có thể sử dụng tệp cấu hình hoặc thực hiện theo chương trình từng bước - nó chỉ phụ thuộc vào yêu cầu của bạn. Điều quan trọng là tôi thường thêm trình xử lý mà tôi muốn vào trình ghi nhật ký gốc, sử dụng các cấp và đôi khi ghi nhật ký. Bộ lọc để nhận các sự kiện tôi muốn vào các tệp thích hợp, bảng điều khiển, nhật ký hệ thống, v.v. Bạn tất nhiên có thể thêm trình xử lý vào bất kỳ trình ghi nhật ký nào khác quá, nhưng theo kinh nghiệm của tôi thì không cần thiết lắm.

Trong mỗi mô-đun, tôi xác định một trình ghi nhật ký bằng cách sử dụng

logger = logging.getLogger(__name__)

và sử dụng nó để ghi các sự kiện trong mô-đun (và, nếu tôi muốn phân biệt thêm), hãy sử dụng một trình ghi nhật ký là con của trình ghi nhật ký được tạo ở trên.

Nếu ứng dụng của tôi có khả năng được sử dụng trong một trang web không định cấu hình đăng nhập settings.py, tôi xác định NullHandler ở đâu đó như sau:

#someutils.py

class NullHandler(logging.Handler):
    def emit(self, record):
        pass

null_handler = NullHandler()

và đảm bảo rằng một phiên bản của nó được thêm vào tất cả các trình ghi nhật ký được tạo trong các mô-đun trong ứng dụng của tôi sử dụng ghi nhật ký. (Lưu ý: NullHandler đã có trong gói ghi nhật ký cho Python 3.1 và sẽ có trong Python 2.7.) Vì vậy:

logger = logging.getLogger(__name__)
logger.addHandler(someutils.null_handler)

Điều này được thực hiện để đảm bảo rằng các mô-đun của bạn hoạt động tốt trong một trang web không định cấu hình đăng nhập settings.py và bạn không nhận được bất kỳ thông báo "Không tìm thấy trình xử lý nào cho trình ghi nhật ký XYZ" gây phiền nhiễu (là những cảnh báo về khả năng ghi nhật ký bị định cấu hình sai).

Làm theo cách này đáp ứng các yêu cầu đã nêu của bạn:

  • Bạn có thể thiết lập các trình xử lý nhật ký khác nhau cho các sự kiện khác nhau như hiện tại.
  • Dễ dàng truy cập vào các trình ghi nhật ký trong mô-đun của bạn - sử dụng getLogger(__name__).
  • Dễ dàng áp dụng cho các mô-đun dòng lệnh - chúng cũng có thể nhập settings.py.

Cập nhật: Lưu ý rằng kể từ phiên bản 1.3, Django hiện đã kết hợp hỗ trợ ghi nhật ký .


Điều này sẽ không yêu cầu mọi mô-đun phải có một trình xử lý được xác định trong cấu hình (bạn không thể sử dụng một trình xử lý cho foo để xử lý foo.bar)? Xem cuộc trò chuyện chúng ta đã có nhiều năm trước tại groups.google.com/group/comp.lang.python/browse_thread/thread/…
andrew cooke

1
@andrew cooke: Bạn có thể sử dụng một trình xử lý foođể xử lý các sự kiện đã đăng nhập foo.bar. Re. luồng đó - cả fileConfig và dictConfig hiện đều có các tùy chọn để ngăn vô hiệu hóa các trình ghi cũ. Thấy vấn đề này: bugs.python.org/issue3136 , mà đến trong một vài tháng sau khi vấn đề của bạn bugs.python.org/issue2697 - dù sao, nó được sắp xếp ra kể từ tháng Sáu năm 2008.
Vinay Sajip

Sẽ tốt hơn nếu làm logger = someutils.getLogger(__name__)ở đâu someutils.getLoggertrả về trình ghi nhật ký logging.getLoggervới một null_handler đã được thêm vào?
7yl4r

1
@ 7yl4r Bạn không cần mọi trình ghi nhật ký phải NullHandlerđược thêm vào - thường chỉ là trình ghi nhật ký cấp cao nhất cho hệ thống phân cấp gói của bạn. Vì vậy, đó sẽ là quá mức cần thiết, IMO.
Vinay Sajip

122

Tôi biết đây là một câu trả lời đã được giải quyết, nhưng theo django> = 1.3 có một cài đặt ghi nhật ký mới.

Chuyển từ cũ sang mới không phải là tự động, vì vậy tôi nghĩ tôi sẽ viết nó ra đây.

Và tất nhiên, hãy kiểm tra tài liệu django để biết thêm.

Đây là conf cơ bản, được tạo theo mặc định với django-admin createproject v1.3 - số dặm có thể thay đổi với các phiên bản django mới nhất:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        }
    }
}

Cấu trúc này dựa trên dictConfig ghi nhật ký Python tiêu chuẩn , ra lệnh cho các khối sau:

  • formatters - giá trị tương ứng sẽ là một dict trong đó mỗi khóa là một id định dạng và mỗi giá trị là một dict mô tả cách cấu hình thể hiện Định dạng tương ứng.
  • filters - giá trị tương ứng sẽ là một dict trong đó mỗi khóa là một id bộ lọc và mỗi giá trị là một dict mô tả cách cấu hình cá thể Bộ lọc tương ứng.
  • handlers- giá trị tương ứng sẽ là một lệnh trong đó mỗi khóa là một id của trình xử lý và mỗi giá trị là một lệnh mô tả cách cấu hình cá thể Trình xử lý tương ứng. Mỗi trình xử lý có các khóa sau:

    • class(bắt buộc). Đây là tên đủ điều kiện của lớp trình xử lý.
    • level(không bắt buộc). Trình độ của người xử lý.
    • formatter(không bắt buộc). Id của định dạng cho trình xử lý này.
    • filters(không bắt buộc). Danh sách id của các bộ lọc cho trình xử lý này.

Tôi thường làm ít nhất điều này:

  • thêm tệp .log
  • định cấu hình các ứng dụng của tôi để ghi vào nhật ký này

Dịch thành:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'null': {
            'level':'DEBUG',
            'class':'django.utils.log.NullHandler',
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        # I always add this handler to facilitate separating loggings
        'log_file':{
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(VAR_ROOT, 'logs/django.log'),
            'maxBytes': '16777216', # 16megabytes
            'formatter': 'verbose'
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
        }
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'apps': { # I keep all my of apps under 'apps' folder, but you can also add them one by one, and this depends on how your virtualenv/paths are set
            'handlers': ['log_file'],
            'level': 'INFO',
            'propagate': True,
        },
    },
    # you can also shortcut 'loggers' and just configure logging for EVERYTHING at once
    'root': {
        'handlers': ['console', 'mail_admins'],
        'level': 'INFO'
    },
}

biên tập

Xem ngoại lệ yêu cầu hiện luôn được ghi lạiVé # 16288 :

Tôi đã cập nhật thông tin mẫu ở trên để bao gồm rõ ràng bộ lọc chính xác cho mail_admins để theo mặc định, email không được gửi khi gỡ lỗi là Đúng.

Bạn nên thêm một bộ lọc:

'filters': {
    'require_debug_false': {
        '()': 'django.utils.log.RequireDebugFalse'
    }
},

và áp dụng nó cho trình xử lý mail_admins:

    'mail_admins': {
        'level': 'ERROR',
        'filters': ['require_debug_false'],
        'class': 'django.utils.log.AdminEmailHandler',
        'include_html': True,
    }

Nếu django.core.handers.base.handle_uncaught_exceptionkhông, lỗi không chuyển đến trình ghi 'django.request' nếu cài đặt.DEBUG là Đúng.

Nếu bạn không làm điều này trong Django 1.5, bạn sẽ nhận được

DeprecationWarning: Bạn không có bộ lọc nào được xác định trên trình xử lý ghi nhật ký 'mail_admins': thêm bộ lọc ngầm gỡ lỗi-false-only

nhưng mọi thứ sẽ vẫn hoạt động chính xác CẢ trong django 1.4 và django 1.5.

** kết thúc chỉnh sửa **

Conf đó được lấy cảm hứng mạnh mẽ từ conf mẫu trong tài liệu django, nhưng thêm phần tệp nhật ký.

Tôi cũng thường làm như sau:

LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'

...
    'level': LOG_LEVEL
...

Sau đó, trong mã python của tôi, tôi luôn thêm một NullHandler trong trường hợp không xác định được thông tin ghi nhật ký nào. Điều này tránh các cảnh báo không có Trình xử lý được chỉ định. Đặc biệt hữu ích cho các lib không nhất thiết chỉ được gọi trong Django (tham khảo )

import logging
# Get an instance of a logger
logger = logging.getLogger(__name__)
class NullHandler(logging.Handler): #exists in python 3.1
    def emit(self, record):
        pass
nullhandler = logger.addHandler(NullHandler())

# here you can also add some local logger should you want: to stdout with streamhandler, or to a local file...

[...]

logger.warning('etc.etc.')

Hi vọng điêu nay co ich!


Stefano, rất cảm ơn vì câu trả lời chi tiết, rất hữu ích. Điều này có thể làm cho nó đáng giá khi nâng cấp lên 1.3.
Diễu hành

Parand, nó chắc chắn là (IMHO!) Đáng để nâng cấp lên django 1.3, mặc dù có một số điểm cần lưu ý để quá trình chuyển đổi suôn sẻ - hãy mở một câu hỏi SO mới nếu bạn gặp khó khăn ;-)
Stefano

Nhân tiện: Tôi vẫn sử dụng loại cài đặt này và nhật ký tệp, nhưng tôi đã chuyển sang sentry để sản xuất!
Stefano

@clime tốt Tôi đã cố gắng giải thích nó trong chính câu trả lời: trong trường hợp không có thông tin đăng nhập nào được xác định. Điều này tránh các cảnh báo không có Trình xử lý được chỉ định. Đặc biệt hữu ích cho libs mà không nhất thiết phải gọi là chỉ trong Django (ref)
Stefano

Tôi không thấy cách bạn sử dụng định nghĩa này: 'null': { 'độ': 'DEBUG', 'lớp': 'django.utils.log.NullHandler',}
khí hậu

9

Chúng tôi khởi tạo đăng nhập ở cấp cao nhất urls.pybằng cách sử dụng một logging.initệp.

Vị trí của logging.iniđược cung cấp trong settings.py, nhưng đó là tất cả.

Mỗi mô-đun sau đó làm

logger = logging.getLogger(__name__)

Để phân biệt các phiên bản thử nghiệm, phát triển và sản xuất, chúng tôi có các tệp logging.ini khác nhau. Đối với hầu hết các phần, chúng tôi có một "nhật ký bảng điều khiển" chỉ dành cho stderr với Lỗi. Chúng tôi có "nhật ký ứng dụng" sử dụng tệp nhật ký cuộn thông thường chuyển đến thư mục nhật ký.


Tôi đã kết thúc bằng cách sử dụng này, ngoại trừ việc khởi tạo trong settings.py thay vì urls.py
Parand vào

Làm cách nào để sử dụng cài đặt từ settings.py trong tệp logging.ini của bạn? Ví dụ: tôi cần cài đặt BASE_DIR, vì vậy tôi có thể cho nó biết nơi lưu trữ các tệp nhật ký của tôi.
slypete

@slypete: Chúng tôi không sử dụng cài đặt trong logging.ini. Vì việc ghi nhật ký chủ yếu là độc lập, chúng tôi không sử dụng bất kỳ cài đặt Django nào. Có, có khả năng lặp lại điều gì đó. Không, nó không tạo ra nhiều khác biệt thực tế.
S.Lott

Trong trường hợp đó, tôi sẽ tạo một tệp logging.ini riêng biệt trong mỗi lần cài đặt ứng dụng của mình.
slypete

@slypete: Bạn có một settings.py cho mỗi cài đặt. Bạn cũng có một logging.ini cho mỗi lần cài đặt. Ngoài ra, bạn cũng có thể có tệp conf Apache cho mỗi lần cài đặt. Cùng với một tệp giao diện wsgi. Tôi không chắc quan điểm của bạn là gì.
S.Lott

6

Tôi hiện đang sử dụng hệ thống ghi nhật ký do chính tôi tạo ra. Nó sử dụng định dạng CSV để ghi nhật ký.

django-csvlog

Dự án này vẫn chưa có tài liệu đầy đủ, nhưng tôi đang thực hiện nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.