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 ee4ca39
Show file tree
Hide file tree
Showing 5 changed files with 53 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

- `handlers` in the `.ini` files don't support `args` anymore. You must you `kwargs`.
Arguments like `args = (sys.stdout,)` becomes `kwargs = {'stream': 'ext://sys.stdout'}`.
- SqlAlchemy logger must be now 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
43 changes: 30 additions & 13 deletions c2cwsgiutils/sqlalchemylogger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,39 @@ 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.

```
[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).

```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 ()
...
```

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 ee4ca39

Please sign in to comment.