Skip to content

Commit

Permalink
feat(utils): add multiline log formatter (reanahub#468)
Browse files Browse the repository at this point in the history
  • Loading branch information
jlemesh authored and tiborsimko committed Oct 21, 2024
1 parent 891aeab commit d40538a
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 2 deletions.
1 change: 1 addition & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The list of contributors in alphabetical order:
- [Elena Gazzarrini](https://orcid.org/0000-0001-5772-5166)
- [Giuseppe Steduto](https://orcid.org/0009-0002-1258-8553)
- [Jan Okraska](https://orcid.org/0000-0002-1416-3244)
- [Jelizaveta Lemeševa](https://orcid.org/0009-0003-6606-9270)
- [Kenyi Hurtado-Anampa](https://orcid.org/0000-0002-9779-3566)
- [Marco Donadoni](https://orcid.org/0000-0003-2922-5505)
- [Marco Vidal](https://orcid.org/0000-0002-9363-4971)
Expand Down
8 changes: 7 additions & 1 deletion reana_job_controller/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from reana_job_controller import config
from reana_job_controller.spec import build_openapi_spec
from reana_job_controller.utils import MultilineFormatter


@event.listens_for(db_engine, "checkin")
Expand Down Expand Up @@ -55,7 +56,12 @@ def shutdown_session(response_or_exc):

def create_app(config_mapping=None):
"""Create REANA-Job-Controller application."""
logging.basicConfig(level=REANA_LOG_LEVEL, format=REANA_LOG_FORMAT)
handler = logging.StreamHandler()
handler.setFormatter(MultilineFormatter(REANA_LOG_FORMAT))
logging.basicConfig(
level=REANA_LOG_LEVEL, format=REANA_LOG_FORMAT, handlers=[handler]
)

app = Flask(__name__)
app.secret_key = "mega secret key"
app.session = Session
Expand Down
28 changes: 27 additions & 1 deletion reana_job_controller/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# This file is part of REANA.
# Copyright (C) 2017, 2018, 2019, 2020, 2022, 2023 CERN.
# Copyright (C) 2017, 2018, 2019, 2020, 2022, 2023, 2024 CERN.
#
# REANA is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.
Expand All @@ -13,11 +13,37 @@
import socket
import subprocess
import sys
from logging import Formatter, LogRecord

from reana_db.database import Session
from reana_db.models import Workflow


class MultilineFormatter(Formatter):
"""Logging formatter for multiline logs."""

def format(self, record: LogRecord):
"""Format multiline log message.
:param record: LogRecord object.
:type record: logging.LogRecord
:return: Formatted log message.
:rtype: str
"""
save_msg = str(record.msg)
output = ""
lines = save_msg.splitlines()
for line in lines:
record.msg = line
output += super().format(record) + "\n"
output = output.strip()
record.msg = save_msg
record.message = output

return output


def singleton(cls):
"""Singelton decorator."""
instances = {}
Expand Down
62 changes: 62 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
#
# This file is part of REANA.
# Copyright (C) 2024 CERN.
#
# REANA is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

import logging
import pytest

from reana_job_controller.utils import MultilineFormatter

"""REANA-Job-Controller utils tests."""


@pytest.mark.parametrize(
"message,expected_output",
[
(
"test",
"name | INFO | test",
),
(
"test\n",
"name | INFO | test",
),
(
"test\ntest",
"name | INFO | test\nname | INFO | test",
),
(
"test\ntest\n\n\n",
"name | INFO | test\nname | INFO | test\nname | INFO | \nname | INFO |",
),
(
" test\ntest ",
"name | INFO | test\nname | INFO | test",
),
(
" t e s\tt\n t e s t ",
"name | INFO | t e s\tt\nname | INFO | t e s t",
),
],
)
def test_multiline_formatter_format(message, expected_output):
"""Test MultilineFormatter formatting."""
formatter = MultilineFormatter("%(name)s | " "%(levelname)s | %(message)s")
assert (
formatter.format(
logging.LogRecord(
"name",
logging.INFO,
"pathname",
1,
message,
None,
None,
),
)
== expected_output
)

0 comments on commit d40538a

Please sign in to comment.