logger
This commit is contained in:
parent
c6cd842b72
commit
ae47946265
5 changed files with 177 additions and 0 deletions
|
|
@ -254,6 +254,44 @@ CACHES = {
|
|||
}
|
||||
}
|
||||
|
||||
LOKI_BASE_PUBLIC_URL = config('LOKI_BASE_PUBLIC_URL', default=None, cast=str)
|
||||
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'loki': {
|
||||
'class': 'utils.logs.LokiFormatter', # required
|
||||
},
|
||||
},
|
||||
|
||||
'handlers': {
|
||||
'loki': {
|
||||
'level': 'DEBUG', # Log level. Required
|
||||
'class': 'utils.logs.LokiHandler', # Required
|
||||
'formatter': 'loki', # Loki formatter. Required
|
||||
'timeout': 2, # Post request timeout, default is 0.5. Optional
|
||||
'url': f'{LOKI_BASE_PUBLIC_URL}/loki/api/v1/push', # Loki url. Defaults to localhost. Optional.
|
||||
# 'auth': ("user", "password"), # Basic auth to authenticate with loki. Default is None (i.e. no auth). Optional
|
||||
'tags': {"app": "ipg"}, # Tags / Labels to attach to the log. Optional, but strongly encoraged to use.
|
||||
'mode': 'thread', # Push mode. Can be 'sync' or 'thread'. Sync is blocking, thread is non-blocking. Defaults to sync. Optional.
|
||||
},
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
# 'formatter': 'verbose',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'handlers': ['console', 'loki'],
|
||||
'level': 'INFO',
|
||||
'propagate': True,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
PNA_CORPORATION_PIN = config('PNA_CORPORATION_PIN')
|
||||
WALLET_BASE_PUBLIC_URL = config('WALLET_BASE_PUBLIC_URL')
|
||||
WALLET_RIAL = config('WALLET_RIAL')
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from rest_framework.views import exception_handler as drf_exception_handler
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
def exception_handler(exc, context):
|
||||
logger.exception(exc)
|
||||
# پاسخ پیشفرض DRF را دریافت میکنیم
|
||||
response = drf_exception_handler(exc, context)
|
||||
|
||||
|
|
|
|||
4
utils/logs/__init__.py
Normal file
4
utils/logs/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
from .formatters import LokiFormatter
|
||||
from .handlers import LokiHandler
|
||||
|
||||
__all__ = ['LokiHandler', 'LokiFormatter']
|
||||
77
utils/logs/formatters.py
Normal file
77
utils/logs/formatters.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import logging
|
||||
import socket
|
||||
from datetime import datetime
|
||||
from logging import BASIC_FORMAT
|
||||
|
||||
|
||||
class LokiFormatter(logging.Formatter):
|
||||
asctime_search = "%(asctime)"
|
||||
tz = "UTC"
|
||||
source = "Loki"
|
||||
src_host = "localhost"
|
||||
tags = {}
|
||||
|
||||
def __init__(self, fmt, dfmt, style, fqdn=False):
|
||||
super(LokiFormatter, self).__init__()
|
||||
|
||||
self.fmt = fmt or BASIC_FORMAT
|
||||
self.dfmt = dfmt or "%Y-%m-%d %H:%M:%S"
|
||||
self.style = style
|
||||
|
||||
if fqdn:
|
||||
self.host = socket.getfqdn()
|
||||
else:
|
||||
self.host = socket.gethostname()
|
||||
|
||||
def format_timestamp(self, time):
|
||||
return str(int(time * 10**9))
|
||||
|
||||
def usesTime(self):
|
||||
return self.fmt.find(self.asctime_search) >= 0
|
||||
|
||||
def formatMessage(self, record):
|
||||
try:
|
||||
return self.fmt % record.__dict__
|
||||
except KeyError as e:
|
||||
raise ValueError("Formatting field not found in record: %s" % e)
|
||||
|
||||
def format(self, record):
|
||||
record.message = record.getMessage()
|
||||
|
||||
if self.usesTime():
|
||||
record.asctime = self.formatTime(record, self.dfmt)
|
||||
|
||||
message = self.formatMessage(record)
|
||||
|
||||
if record.exc_info:
|
||||
if not record.exc_text:
|
||||
record.exc_text = self.formatException(record.exc_info)
|
||||
|
||||
if record.exc_text:
|
||||
if message[-1:] != "\n":
|
||||
message = message + "\n"
|
||||
message = message + record.exc_text
|
||||
|
||||
if record.stack_info:
|
||||
if message[-1:] != "\n":
|
||||
message = message + "\n"
|
||||
|
||||
message = message + self.formatStack(record.stack_info)
|
||||
|
||||
message = {
|
||||
"streams": [
|
||||
{
|
||||
"stream": {
|
||||
**self.tags,
|
||||
},
|
||||
"values": [
|
||||
[
|
||||
self.format_timestamp(record.created),
|
||||
message,
|
||||
]
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return message
|
||||
57
utils/logs/handlers.py
Normal file
57
utils/logs/handlers.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import logging
|
||||
import sys
|
||||
from threading import Thread
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class LokiHandler(logging.Handler):
|
||||
def __init__(
|
||||
self,
|
||||
timeout=0.5,
|
||||
url="http://localhost:3100/loki/api/v1/push",
|
||||
auth=None,
|
||||
tags={},
|
||||
mode="sync",
|
||||
):
|
||||
super(LokiHandler, self).__init__()
|
||||
|
||||
self._url = url
|
||||
self._timeout = timeout
|
||||
self._auth = auth
|
||||
self._tags = tags
|
||||
|
||||
self._mode = mode
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
payload = self.formatter.format(record)
|
||||
|
||||
self._push_message(
|
||||
self._url, json=payload, timeout=self._timeout, auth=self._auth
|
||||
)
|
||||
except requests.exceptions.ReadTimeout:
|
||||
sys.stderr.write("Loki connection timed out\n")
|
||||
except Exception as e:
|
||||
sys.stderr.write(f"Loki connection failed with: {e}\n")
|
||||
|
||||
def setFormatter(self, fmt):
|
||||
fmt.tags = self._tags
|
||||
|
||||
self.formatter = fmt
|
||||
|
||||
def _push_message(self, *args, **kwargs):
|
||||
if self._mode == "sync":
|
||||
return _push_message(*args, **kwargs)
|
||||
|
||||
if self._mode == "thread":
|
||||
return Thread(target=_push_message, args=args, kwargs=kwargs).start()
|
||||
|
||||
|
||||
def _push_message(*args, **kwargs):
|
||||
response = requests.post(*args, **kwargs)
|
||||
|
||||
if response.status_code != 204:
|
||||
sys.stderr.write(
|
||||
f"Got status {response.status_code} from loki with message: {response.text}\n"
|
||||
)
|
||||
Loading…
Add table
Reference in a new issue