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')
|
PNA_CORPORATION_PIN = config('PNA_CORPORATION_PIN')
|
||||||
WALLET_BASE_PUBLIC_URL = config('WALLET_BASE_PUBLIC_URL')
|
WALLET_BASE_PUBLIC_URL = config('WALLET_BASE_PUBLIC_URL')
|
||||||
WALLET_RIAL = config('WALLET_RIAL')
|
WALLET_RIAL = config('WALLET_RIAL')
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from rest_framework.views import exception_handler as drf_exception_handler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
def exception_handler(exc, context):
|
def exception_handler(exc, context):
|
||||||
|
logger.exception(exc)
|
||||||
# پاسخ پیشفرض DRF را دریافت میکنیم
|
# پاسخ پیشفرض DRF را دریافت میکنیم
|
||||||
response = drf_exception_handler(exc, context)
|
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