Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logging config #2

Merged
merged 3 commits into from
Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<img alt="StarLite logo" src="./starlite-logo.svg" width=100%, height="auto">

![Discord](https://img.shields.io/discord/919193495116337154?logo=discord)

# starlite

Pre-Alpha WIP.
8 changes: 5 additions & 3 deletions docs/0-introduction.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Starlite
# StarLite

Starlite is a flexible and extensible ASGI API framework built on top of Starlette and Pydantic.
Starlite is a simple, flexible and extensible ASGI API framework built on top of Starlette and Pydantic. It was inspired
by FastAPI and NestJS.

## Example: Controller Pattern

Starlite supports class API components called "Controllers". Controllers are meant to group logical subcomponents, for example - consider the following `UserController`:
Starlite supports class API components called "Controllers". Controllers are meant to group logical subcomponents, for
example - consider the following `UserController`:

```python3
from pydantic import BaseModel, UUID4
Expand Down
26 changes: 26 additions & 0 deletions docs/usage/0-the-starlite-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# The Starlite App

At the root of every starlite application is an instance of the `Starlite` class or a subclass of it:

Instantiating the app is straightforward:

```python
from starlite import Starlite

app = Starlite()
```

The `Starlite` class supports the following (optional) kwargs:

* `debug`: a boolean flag toggling debug mode on and off
* `middleware`: a sequence of `Middleware` subclasses
* `exception_handlers`: a dictionary mapping exceptions or exception codes to callables
* `route_handlers`: a sequence of route handlers
* `on_startup`: a sequence of callables to be called during the application startup
* `on_shutdown`: a sequence of callables to be called during the application shutdown
* `lifespan`: an async-context that handles startup and shutdown
* `dependencies`: a dictionary mapping keys to dependency providers
* `logging_config`: either a subclass of `starlite.logging.LoggingConfig` or None (disable logging)

> :warning: **Warning**: debug should not be used in production
> :warning: **Warning**: you can specify either `on_startup`/`on_shutdown` or `lifespan` but not both
Empty file.
Empty file added docs/usage/2-life-cycle.md
Empty file.
Empty file.
Empty file added docs/usage/4-middleware.md
Empty file.
Empty file added docs/usage/5-exceptions.md
Empty file.
Empty file added docs/usage/6-testing.md
Empty file.
5 changes: 5 additions & 0 deletions starlite/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing_extensions import Type

from starlite.handlers import RouteHandler
from starlite.logging import LoggingConfig
from starlite.provide import Provide
from starlite.routing import Router
from starlite.utils import DeprecatedProperty
Expand All @@ -27,6 +28,7 @@
class Starlite(Starlette):
def __init__( # pylint: disable=super-init-not-called
self,
*,
debug: bool = False,
middleware: Sequence[Middleware] = None,
exception_handlers: Dict[Union[int, Type[Exception]], Callable] = None,
Expand All @@ -35,7 +37,10 @@ def __init__( # pylint: disable=super-init-not-called
on_shutdown: Optional[Sequence[Callable]] = None,
lifespan: Optional[Callable[[Any], AsyncContextManager]] = None,
dependencies: Optional[Dict[str, Provide]] = None,
logging_config: Optional[LoggingConfig] = LoggingConfig()
):
if logging_config:
logging_config.configure(debug)
self._debug = debug
self.state = State()
self.router = Router(
Expand Down
32 changes: 32 additions & 0 deletions starlite/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from logging import config
from typing import Dict, Optional, Union

from pydantic import BaseModel
from typing_extensions import Literal


class LoggingConfig(BaseModel):
version: Literal[1] = 1
incremental: bool = False
disable_existing_loggers: bool = False
filters: Optional[Dict[str, dict]] = None
formatters: Dict[str, dict] = {
"standard": {"format": "%(levelname)s - %(asctime)s - %(name)s - %(module)s - %(message)s"}
}
handlers: Dict[str, dict] = {
"console": {"class": "logging.StreamHandler", "level": "DEBUG", "formatter": "standard"}
}
loggers: Dict[str, dict] = {
"starlite": {
"level": "INFO",
"handlers": ["console"],
},
}
root: Dict[str, Union[dict, list, str]] = {"handlers": ["console"], "level": "WARNING"}

def configure(self, debug: bool = False):
"""Configure logging by converting 'self' to dict and passing it to logging.config.dictConfig"""
logging_config = self.dict(exclude_none=True)
if debug and "starlite" in logging_config["loggers"]:
logging_config["loggers"]["starlite"]["level"] = "DEBUG"
config.dictConfig(logging_config)
1 change: 1 addition & 0 deletions starlite/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class Router(StarletteRouter):

def __init__(
self,
*,
path: str,
route_handlers: Optional[Sequence[Union[Type[Controller], RouteHandler, "Router", Callable]]] = None,
redirect_slashes: bool = True,
Expand Down
13 changes: 13 additions & 0 deletions tests/test_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from unittest.mock import Mock, patch

from starlite.logging import LoggingConfig


@patch("logging.config.dictConfig")
def test_logging_debug(dictConfigMock: Mock):
config = LoggingConfig()
config.configure()
assert dictConfigMock.mock_calls[0][1][0]["loggers"]["starlite"]["level"] == "INFO"
dictConfigMock.reset_mock()
config.configure(debug=True)
assert dictConfigMock.mock_calls[0][1][0]["loggers"]["starlite"]["level"] == "DEBUG"