-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Productionize log configuration (#86)
* Add a JsonFormatter (similar to identity-uw formatter) so that stackdriver logs are useful. * [Bot] Update version to 1.1.0 Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
e0d8826
commit 2ff7c80
Showing
6 changed files
with
112 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import json | ||
import logging | ||
from typing import Optional, Type, TypeVar | ||
|
||
from flask import Request | ||
from injector import Injector | ||
from werkzeug.local import LocalProxy | ||
|
||
T = TypeVar("T") | ||
|
||
|
||
class JsonFormatter(logging.Formatter): | ||
""" | ||
A formatter adhering to the structure advised in | ||
https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry. | ||
""" | ||
|
||
injector: Injector = None | ||
|
||
def _get_optional_injected(self, cls: Type[T]) -> Optional[T]: | ||
if not self.injector: | ||
return None | ||
try: | ||
return self.injector.get(cls) | ||
except RuntimeError: | ||
return None | ||
|
||
def get_request(self) -> Optional[Request]: | ||
return self._get_optional_injected(Request) | ||
|
||
def get_session(self) -> Optional[LocalProxy]: | ||
return self._get_optional_injected(LocalProxy) | ||
|
||
def format(self, record): | ||
data = { | ||
"severity": record.levelname, | ||
"message": record.getMessage(), | ||
"module": record.module, | ||
"line": f"{record.filename}#{record.funcName}:{record.lineno}", | ||
} | ||
request = self.get_request() | ||
if request: | ||
request_log = { | ||
"method": request.method, | ||
"url": request.url, | ||
"remoteIp": request.remote_addr, | ||
} | ||
session = self.get_session() | ||
if session and session.get("uwnetid"): | ||
request_log["uwnetid"] = session["uwnetid"] | ||
data["request"] = request_log | ||
|
||
return json.dumps(data, default=str) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[tool.poetry] | ||
name = "uw-husky-directory" | ||
version = "1.0.2" | ||
version = "1.1.0" | ||
description = "An updated version of the UW Directory" | ||
authors = ["Thomas Thorogood <[email protected]>"] | ||
license = "MIT" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from unittest import mock | ||
from unittest.mock import MagicMock | ||
|
||
from flask import Request | ||
from injector import Injector | ||
from werkzeug.local import LocalProxy | ||
|
||
from husky_directory.logging import JsonFormatter | ||
|
||
|
||
def test_get_attrs_no_injector(): | ||
formatter = JsonFormatter() | ||
assert formatter.get_session() is None | ||
assert formatter.get_request() is None | ||
|
||
|
||
def test_get_attrs_injector_error(injector: Injector): | ||
formatter = JsonFormatter() | ||
formatter.injector = injector | ||
with mock.patch.object(injector, "get") as mock_get: | ||
mock_get.side_effect = RuntimeError | ||
assert formatter.get_session() is None | ||
assert formatter.get_request() is None | ||
|
||
|
||
def test_formatter_late_injection(injector: Injector, client, mock_injected): | ||
formatter = JsonFormatter() | ||
formatter.injector = injector | ||
with mock_injected(LocalProxy, {}) as session: | ||
request = MagicMock(Request) | ||
request.method = "GET" | ||
request.url = "https://www.uw.edu" | ||
request.remote_addr = "127.0.0.1" | ||
session["uwnetid"] = "dawg" | ||
with mock_injected(Request, request): | ||
client.get("/") | ||
assert formatter.get_request().url == "https://www.uw.edu" | ||
assert formatter.get_session().get("uwnetid") == "dawg" |