Skip to content

Commit

Permalink
Fix parsing of the ini file to setup loggers
Browse files Browse the repository at this point in the history
Done for sqlalchemylogger, but actually the logger would try
to write logs (process) on another process than the logs
collecteur (emit) which can't work. The setup of sqlalchemy
logger must be done manually in the project and not via the
ini file.
  • Loading branch information
ger-benjamin committed Jun 14, 2024
1 parent b88f762 commit a7fdc19
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 24 deletions.
7 changes: 7 additions & 0 deletions BREAKING_CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Release 6.1

- The `handlers` in the `.ini` files don't support `args` anymore. You must use `kwargs`
arguments. Example `args = (sys.stdout,)` becomes `kwargs = {'stream': 'ext://sys.stdout'}`.
- SqlAlchemy logger must now be instantiated by your app's `main` method and not by your
`.ini` file. Read the example in the sqlalchemylogger folder.

## Release 6.0

- The stats will not anymore be published on StatsD, use Prometheus client instead.
Expand Down
4 changes: 2 additions & 2 deletions acceptance_tests/app/production.ini
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ qualname = c2cwsgiutils_app

[handler_console]
class = logging.StreamHandler
args = (sys.stdout,)
kwargs = {'stream': 'ext://sys.stdout'}
level = NOTSET
formatter = generic

[handler_json]
class = c2cwsgiutils.pyramid_logging.JsonLogHandler
args = (sys.stdout,)
kwargs = {'stream': 'ext://sys.stdout'}
level = NOTSET
formatter = generic

Expand Down
19 changes: 12 additions & 7 deletions c2cwsgiutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import os
import re
import ast
import sys
from configparser import SectionProxy
from typing import Any
Expand Down Expand Up @@ -35,20 +36,24 @@ def _create_handlers(config: configparser.ConfigParser) -> dict[str, Any]:
stream_re = re.compile(r"\((.*?),\)")
for hh in handlers:
block = config[f"handler_{hh}"]
stream_match = stream_re.match(block["args"])
if stream_match is None:
raise Exception(f"Could not parse args of handler {hh}") # pylint: disable=broad-exception-raised
args = stream_match.groups()[0]
if "args" in block:
raise ValueError(f"Can not parse args of handlers {hh}, use kwargs instead.")
c = block["class"]
if "." not in c:
# classes like StreamHandler does not need the prefix in the ini so we add it here
c = f"logging.{c}"
conf = {
"class": c,
"stream": f"ext://{args}", # like ext://sys.stdout
"class": c,
}
if "level" in block:
conf["level"] = block["level"]
if "formatter" in block:
conf["formatter"] = block["formatter"]
if "filters" in block:
conf["filters"] = block["filters"]
if "kwargs" in block:
kwargs = ast.literal_eval(block["kwargs"])
conf.update(kwargs)
d_handlers[hh] = conf
return d_handlers

Expand All @@ -57,7 +62,7 @@ def _filter_logger(block: SectionProxy) -> dict[str, Any]:
out: dict[str, Any] = {"level": block["level"]}
handlers = block.get("handlers", "")
if handlers != "":
out["handlers"] = [block["handlers"]]
out["handlers"] = [k.strip() for k in block["handlers"].split(",")]
return out


Expand Down
42 changes: 29 additions & 13 deletions c2cwsgiutils/sqlalchemylogger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,38 @@ This module is used to ship logging records to an SQL database.

Currently only `sqlite` and `postgres_psycopg2` are fully supported.

To add the logger in a pyramid ini file use something like:
To add the handler, setup it directly in your app's main function. You
can add it to an existing logger (setup in you `.ini` file),
or create a new logger by calling the `logging.getlogger` method.

```python
import logging
from c2cwsgiutils.sqlalchemylogger.handlers import SQLAlchemyHandler

def _setup_sqlalchemy_logger():
"""
Setup sqlalchemy logger.
"""
logger = logging.getLogger("A_LOGGER")
handler = SQLAlchemyHandler(
sqlalchemy_url={
# "url": "sqlite:///logger_db.sqlite3",
"url": "postgresql://postgres:password@localhost:5432/test",
"tablename": "test",
"tableargs": {"schema": "xyz"},
},
does_not_contain_expression="curl",
)
logger.addHandler(handler)

def main(_, **settings):
_setup_sqlalchemy_logger ()
...
```
[handlers]
keys = sqlalchemy_logger
[handler_sqlalchemy_logger]
class = c2cwsgiutils.sqlalchemylogger.handlers.SQLAlchemyHandler
#args = ({'url':'sqlite:///logger_db.sqlite3','tablename':'test'},'curl')
args = ({'url':'postgresql://postgres:password@localhost:5432/test','tablename':'test','tableargs': {'schema':'xyz'}},'curl')
level = NOTSET
formatter = generic
propagate = 0
```
Do not set up this sqlalchemy logger in you `.ini` file directly.
It won't work (multi process issue).

if the credentials given in `args = ` section are sufficient, the handler will
if the given credentials are sufficient, the handler will
create the DB, schema and table it needs directly.

In the above example the second parameter provided `'curl'` is a negative
Expand Down
4 changes: 2 additions & 2 deletions production.ini
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ qualname = c2cwsgiutils

[handler_console]
class = logging.StreamHandler
args = (sys.stdout,)
kwargs = {'stream': 'ext://sys.stdout'}
level = NOTSET
formatter = generic

[handler_json]
class = c2cwsgiutils.pyramid_logging.JsonLogHandler
args = (sys.stdout,)
kwargs = {'stream': 'ext://sys.stdout'}
level = NOTSET
formatter = generic

Expand Down

0 comments on commit a7fdc19

Please sign in to comment.