Commit a351ec86 authored by Paweł Wieczorek's avatar Paweł Wieczorek
Browse files

Merge branch 'collabora-staging-json-logging' into 'collabora/staging'

JSON logging to stdout test

See merge request !72
parents 2d09b73e bdea8be9
Pipeline #42223 waiting for manual action with stages
in 19 minutes and 13 seconds
......@@ -34,6 +34,9 @@ RUN apt update && \
wait-for-it && \
rm /*deb
# Logging env
COPY docker/collabora/logging_env.yaml /
# We use configuration from the environment, not an env file, but we need this empty file
# to silence a warning from django-environ.
RUN touch /usr/lib/python3/dist-packages/lava_server/settings/.env
......
LOGGING:
version: 1
disable_existing_loggers: True
filters:
require_debug_false:
(): django.utils.log.RequireDebugFalse
formatters:
json_formatter:
(): lava_common.logging_utils.json_formatter
extra_keys:
- sql
- duration
- alias
handlers:
console:
class: logging.StreamHandler
formatter: json_formatter
level: DEBUG
loggers:
django:
handlers:
- console
level: ERROR
propagate: False
django_auth_ldap:
handlers:
- console
level: INFO
propagate: False
lava_results_app:
handlers:
- console
level: INFO
propagate: False
lava_scheduler_app:
handlers:
- console
level: INFO
propagate: False
lava-scheduler:
handlers:
- console
level: DEBUG
propagate: False
lava-publisher:
handlers:
- console
level: DEBUG
propagate: False
......@@ -9,6 +9,7 @@ DATABASE=`python3 -c 'import os; from urllib.parse import urlparse; url = urlpar
# Make sure configuration directories are available and have the appropriate
# ownership and permissions.
mkdir -p /etc/lava-server/settings.d
cp /logging_env.yaml /etc/lava-server/settings.d
mkdir -p /etc/lava-server/dispatcher.d
chown -R lavaserver: /etc/lava-server/dispatcher.d
......@@ -36,10 +37,10 @@ if [ -z $LOGLEVEL ]; then
LOGLEVEL=DEBUG
fi
/usr/bin/lava-server manage lava-publisher --level $LOGLEVEL --log-file - --host '*' --port 8001 &
/usr/bin/lava-server manage lava-publisher --host '*' --port 8001 &
while :; do
/usr/bin/lava-server manage lava-scheduler --level $LOGLEVEL --log-file - $EVENT_URL $IPV6
/usr/bin/lava-server manage lava-scheduler $EVENT_URL $IPV6
done &
if [ -z $WORKERS ]; then
......@@ -54,4 +55,4 @@ if [ -z $BIND ]; then
BIND="--bind 0.0.0.0:8000"
fi
exec /usr/bin/gunicorn3 lava_server.wsgi --log-level $LOGLEVEL --log-file - -u lavaserver -g lavaserver --worker-class $WORKER_CLASS --workers $WORKERS $BIND $RELOAD $TIMEOUT
exec /usr/bin/gunicorn3 lava_server.wsgi -u lavaserver -g lavaserver --worker-class $WORKER_CLASS --workers $WORKERS $BIND $RELOAD $TIMEOUT
......@@ -4,7 +4,7 @@ After=network.target remote-fs.target
[Service]
Type=simple
Environment=LOGLEVEL=DEBUG LOGFILE=/var/log/lava-server/lava-publisher.log HOST="*" PORT=8001
Environment=HOST="*" PORT=8001
EnvironmentFile=-/etc/default/lava-publisher
EnvironmentFile=-/etc/lava-server/lava-publisher
ExecStart=/usr/bin/lava-server manage lava-publisher --level $LOGLEVEL --log-file $LOGFILE --host $HOST --port $PORT
......
......@@ -4,7 +4,6 @@ After=network.target remote-fs.target
[Service]
Type=simple
Environment=LOGLEVEL=DEBUG LOGFILE=/var/log/lava-server/lava-scheduler.log
EnvironmentFile=-/etc/default/lava-scheduler
EnvironmentFile=-/etc/lava-server/lava-scheduler
ExecStart=/usr/bin/lava-server manage lava-scheduler --level $LOGLEVEL --log-file $LOGFILE $EVENT_URL $IPV6
......
# Copyright (C) 2022 Collabora
#
# Author: Igor Ponomarev <igor.ponomarev@collabora.com>
#
# This file is part of LAVA.
#
# LAVA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LAVA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along
# with this program; if not, see <http://www.gnu.org/licenses>.
from __future__ import annotations
from logging import Formatter, LogRecord
from json import dumps as json_dumps
from typing import FrozenSet, Optional, List
class JsonFormatter(Formatter):
DEFAULT_KEYS: FrozenSet[str] = frozenset(
(
"args",
"exc_info", # Exception info
"exc_text",
"msg", # Log message
"name", # Logger name
"pathname", # Log location
"lineno",
"stack_info",
"levelname", # Level name (DEBUG, INFO...)
)
)
def __init__(self, *args, extra_keys: Optional[List[str]] = None, **kwargs):
super().__init__(*args, **kwargs)
if extra_keys is not None:
extra_keys = set(extra_keys)
else:
extra_keys = set()
self.allowed_keys = self.DEFAULT_KEYS | set(extra_keys)
def filter_log_record_field(self, key: str, value: object) -> bool:
if key not in self.allowed_keys:
return False
return True
def format(self, record: LogRecord) -> str:
logrecord_dict = {
k: v
for k, v in record.__dict__.items()
if self.filter_log_record_field(k, v)
}
try:
args = logrecord_dict.pop("args")
except KeyError:
args = ()
logrecord_dict["msg"] %= args
logrecord_dict["asctime"] = self.formatTime(record)
return json_dumps(logrecord_dict)
def json_formatter(*args, **kwargs):
return JsonFormatter(*args, **kwargs)
......@@ -28,9 +28,14 @@ import os
import pwd
import signal
from typing import Optional
from django.core.management.base import BaseCommand
DEFAULT_LAVA_DAEMON_LOG_FORMAT = "%(asctime)-15s %(levelname)7s %(message)s"
class LAVADaemonCommand(BaseCommand):
def add_arguments(self, parser):
log = parser.add_argument_group("logging")
......@@ -38,12 +43,15 @@ class LAVADaemonCommand(BaseCommand):
"-l",
"--level",
choices=["ERROR", "WARN", "INFO", "DEBUG"],
default="DEBUG",
nargs="?",
help="Logging level (ERROR, WARN, INFO, DEBUG) " "Default: DEBUG",
)
log.add_argument(
"-o", "--log-file", default=self.default_logfile, help="Logging file path"
"-o",
"--log-file",
nargs="?",
help="Logging file path",
)
priv = parser.add_argument_group("privileges")
......@@ -88,27 +96,42 @@ class LAVADaemonCommand(BaseCommand):
return True
def setup_logging(self, logger_name, level, log_file, log_format):
del logging.root.handlers[:]
del logging.root.filters[:]
def setup_logging(
self,
logger_name: str,
level: Optional[str] = None,
log_file: Optional[str] = None,
log_format: Optional[str] = None,
) -> None:
# Create the logger
self.logger = logging.getLogger(logger_name)
if log_file == "-":
handler = logging.StreamHandler()
else:
handler = logging.handlers.WatchedFileHandler(log_file)
handler.setFormatter(logging.Formatter(log_format))
self.logger.addHandler(handler)
if log_file is not None:
# Remove existing handlers
self.logger.handlers.clear()
if log_file == "-":
handler = logging.StreamHandler()
else:
handler = logging.handlers.WatchedFileHandler(log_file)
self.logger.addHandler(handler)
if log_format is None:
log_format = DEFAULT_LAVA_DAEMON_LOG_FORMAT
handler.setFormatter(logging.Formatter(log_format))
# Set log level
if level == "ERROR":
self.logger.setLevel(logging.ERROR)
elif level == "WARN":
self.logger.setLevel(logging.WARN)
elif level == "INFO":
self.logger.setLevel(logging.INFO)
else:
self.logger.setLevel(logging.DEBUG)
if level is not None:
if level == "ERROR":
self.logger.setLevel(logging.ERROR)
elif level == "WARN":
self.logger.setLevel(logging.WARN)
elif level == "INFO":
self.logger.setLevel(logging.INFO)
else:
self.logger.setLevel(logging.DEBUG)
def setup_zmq_signal_handler(self):
# Mask signals and create a pipe that will receive a bit for each
......
......@@ -34,7 +34,6 @@ from lava_server.cmdutils import LAVADaemonCommand
TIMEOUT = 5
FORMAT = "%(asctime)-15s %(levelname)7s %(message)s"
async def zmq_proxy(app):
......@@ -164,9 +163,7 @@ class Command(LAVADaemonCommand):
parser.add_argument("--port", default=8001, type=int, help="port to bind to")
def handle(self, *args, **options):
self.setup_logging(
"lava-publisher", options["level"], options["log_file"], FORMAT
)
self.setup_logging("lava-publisher", options["level"], options["log_file"])
self.logger.info("[INIT] Starting lava-publisher")
self.logger.info("[INIT] Version %s", __version__)
......
......@@ -44,9 +44,6 @@ from lava_server.cmdutils import LAVADaemonCommand
INTERVAL = 20
PING_TIMEOUT = 3 * INTERVAL
# Log format
FORMAT = "%(asctime)-15s %(levelname)7s %(message)s"
class Command(LAVADaemonCommand):
logger = None
......@@ -87,7 +84,9 @@ class Command(LAVADaemonCommand):
def handle(self, *args, **options):
# Initialize logging.
self.setup_logging(
"lava-scheduler", options["level"], options["log_file"], FORMAT
"lava-scheduler",
options["level"],
options["log_file"],
)
self.logger.info("[INIT] Starting lava-scheduler")
......
......@@ -207,6 +207,8 @@ CUSTOM_DOCS = {}
# Logging
DJANGO_LOGFILE = "/var/log/lava-server/django.log"
LAVA_SCHEDULER_LOG_FILE = "/var/log/lava-server/lava-scheduler.log"
LAVA_PUBLISHER_LOG_FILE = "/var/log/lava-server/lava-publisher.log"
# Configuration directories
DISPATCHER_CONFIG_PATH = "/etc/lava-server/dispatcher.d"
......@@ -339,7 +341,11 @@ def update(values):
AUTH_GITLAB_SCOPE = values.get("AUTH_GITLAB_SCOPE")
AUTHENTICATION_BACKENDS = values.get("AUTHENTICATION_BACKENDS")
DISALLOWED_USER_AGENTS = values.get("DISALLOWED_USER_AGENTS")
DJANGO_LOGFILE = values.get("DJANGO_LOGFILE")
LAVA_SCHEDULER_LOG_FILE = values.get("LAVA_SCHEDULER_LOG_FILE")
LAVA_PUBLISHER_LOG_FILE = values.get("LAVA_PUBLISHER_LOG_FILE")
EVENT_NOTIFICATION = values.get("EVENT_NOTIFICATION")
INSTALLED_APPS = values.get("INSTALLED_APPS")
INTERNAL_IPS = values.get("INTERNAL_IPS")
......@@ -474,7 +480,13 @@ def update(values):
"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}
},
"formatters": {
"lava": {"format": "%(levelname)s %(asctime)s %(module)s %(message)s"}
"lava": {"format": "%(levelname)s %(asctime)s %(module)s %(message)s"},
"lava-scheduler": {
"format": "%(asctime)-15s %(levelname)7s %(message)s"
},
"lava-publisher": {
"format": "%(asctime)-15s %(levelname)7s %(message)s"
},
},
"handlers": {
"console": {
......@@ -487,6 +499,16 @@ def update(values):
"filename": DJANGO_LOGFILE,
"formatter": "lava",
},
"lava-scheduler-logfile": {
"class": "logging.handlers.WatchedFileHandler",
"filename": LAVA_SCHEDULER_LOG_FILE,
"formatter": "lava-scheduler",
},
"lava-publisher-logfile": {
"class": "logging.handlers.WatchedFileHandler",
"filename": LAVA_PUBLISHER_LOG_FILE,
"formatter": "lava-publisher",
},
},
"loggers": {
"django": {
......@@ -510,6 +532,16 @@ def update(values):
"level": "INFO",
"propagate": True,
},
"lava-scheduler": {
"handlers": ["lava-scheduler-logfile"],
"level": "DEBUG",
"propagate": False,
},
"lava-publisher": {
"handlers": ["lava-publisher-logfile"],
"level": "DEBUG",
"propagate": False,
},
},
}
......
......@@ -44,6 +44,8 @@ PROJECT_STATE_DIR.mkdir(parents=True, exist_ok=True)
# LAVA logs
DJANGO_LOGFILE = str(PROJECT_STATE_DIR / "django.log")
LAVA_SCHEDULER_LOG_FILE = str(PROJECT_STATE_DIR / "lava-scheduler.log")
LAVA_PUBLISHER_LOG_FILE = str(PROJECT_STATE_DIR / "lava-publisher.log")
# Test database
DATABASES = {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment