Skip to content

Commit

Permalink
feature: Update to google_cloud_logger v0.1.0 (#1)
Browse files Browse the repository at this point in the history
* feature: Update to google_cloud_logger v0.1.0
* chore: Bump lib version to 0.1.0
  • Loading branch information
rai200890 authored Nov 4, 2018
1 parent a2d9305 commit 1f19c31
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 62 deletions.
2 changes: 2 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ name = "pypi"

[packages]
flask_google_cloud_logger = {editable = true, path = "."}
flask = "*"

[dev-packages]
pytest = "~=3.9.3"
Expand All @@ -14,5 +15,6 @@ flake8 = "~=3.6.0"
yapf = "~=0.24.0"
ipdb = "~=0.11"
safety = "~=1.8.4"

[requires]
python_version = "3.6"
29 changes: 16 additions & 13 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 81 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,86 @@

Flask extension to format logs according to Google Cloud v2 Specification

Python log formatter for Google Cloud according to [v2 specification](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry) using [python-json-logger](https://github.com/madzak/python-json-logger) formatter

Inspired by Elixir's [logger_json](https://github.com/Nebo15/logger_json)

## Instalation

### Pipenv

```
pipenv install flask_google_cloud_logger
```

### Pip

```
pip install flask_google_cloud_logger
```

## Usage

```python
import logging
from logging import config

from flask import Flask, request, g
from flask_google_cloud_logger import FlaskGoogleCloudLogger


LOG_CONFIG = {
"version": 1,
"formatters": {
"json": {
"()": "flask_google_cloud_logger.FlaskGoogleCloudFormatter",
"application_info": {
"type": "python-application",
"name": "Example Application"
},
"format": "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
}
},
"handlers": {
"json": {
"class": "logging.StreamHandler",
"formatter": "json"
}
},
"loggers": {
"root": {
"level": "INFO",
"handlers": ["json"]
},
"werkzeug": {
"level": "WARN", # Disable werkzeug hardcoded logger
"handlers": ["json"]
}
}
}

config.dictConfig(LOG_CONFIG) # load log config from dict
logger = logging.getLogger("root") # get root logger instance
app = Flask("test_app")
FlaskGoogleCloudLogger(app)


@app.route("/")
def hello_world():
return "Hello, World!"


@app.teardown_request
def log_request_time(_exception):
logger.info(f"{request.method} {request.path} - Sent {g.response.status_code} in {g.request_time:.5f}ms")
```

Example output:

```json
{"timestamp": "2018-11-04T21:34:04.002000Z", "severity": "INFO", "message": "GET / - Sent 200 in 0.13828ms", "labels": {"client": {"user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36", "ip": "127.0.0.1", "version": null}, "connection": {"method": "GET", "path": "/", "request_id": "9135a0fc-8398-40a7-b830-47f6265672a2", "status": 200}, "latency": 0.13828277587890625}, "metadata": {"userLabels": {}}, "sourceLocation": {"file": "test_app.py", "line": 51, "function": "log_request_time"}}
```

## Credits

Thanks [@thulio](https://github.com/thulio), [@robsonpeixoto](https://github.com/robsonpeixoto), [@ramondelemos](https://github.com/ramondelemos)
Thanks [@thulio](https://github.com/thulio), [@robsonpeixoto](https://github.com/robsonpeixoto), [@ramondelemos](https://github.com/ramondelemos)
52 changes: 52 additions & 0 deletions examples/test_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import logging
from logging import config

from flask import Flask, request, g
from flask_google_cloud_logger import FlaskGoogleCloudLogger

LOG_CONFIG = {
"version": 1,
"formatters": {
"json": {
"()": "flask_google_cloud_logger.FlaskGoogleCloudFormatter",
"application_info": {
"type": "python-application",
"name": "Example Application"
},
"format": "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
}
},
"handlers": {
"json": {
"class": "logging.StreamHandler",
"formatter": "json"
}
},
"loggers": {
"root": {
"level": "INFO",
"handlers": ["json"]
},
"werkzeug": {
"level": "WARN", # Disable werkzeug hardcoded logger
"handlers": ["json"]
}
}
}

config.dictConfig(LOG_CONFIG) # load log config from dict
logger = logging.getLogger("root") # get root logger instance
app = Flask("test_app")
FlaskGoogleCloudLogger(app)


@app.route("/")
def hello_world():
return "Hello, World!"


@app.teardown_request
def log_request_time(_exception):
logger.info(
f"{request.method} {request.path} - Sent {g.response.status_code}" +
" in {g.request_time:.5f}ms")
6 changes: 3 additions & 3 deletions flask_google_cloud_logger/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from . import hooks
from . import formatter # noqa
from .formatter import FlaskGoogleCloudFormatter # noqa


class FlaskGoogleCloudLogger(object):
Expand All @@ -18,5 +18,5 @@ def teardown(self, exception):
"flask_google_cloud_logger_after_request", None)

def init_app(self, app):
app.before_request(hooks.flask_google_cloud_logger_before_request)
app.after_request(hooks.flask_google_cloud_logger_after_request)
self.app.before_request(hooks.flask_google_cloud_logger_before_request)
self.app.after_request(hooks.flask_google_cloud_logger_after_request)
13 changes: 7 additions & 6 deletions flask_google_cloud_logger/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@


class FlaskGoogleCloudFormatter(GoogleCloudFormatter):
def make_metadata(self, _record):
def __init__(self, *args, **kwargs):
super(FlaskGoogleCloudFormatter, self).__init__(*args, **kwargs)

def make_labels(self):
if has_request_context():
return {
"userLabels": {
"client": self._make_client_info(request),
"connection": self._make_connection_info(request, g),
"latency": getattr(g, "request_time", None)
}
"client": self._make_client_info(request),
"connection": self._make_connection_info(request, g),
"latency": getattr(g, "request_time", None)
}
return {}

Expand Down
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
from setuptools import setup

__VERSION__ = "0.1.0"

setup(
name="flask_google_cloud_logger",
version="0.0.2",
version=__VERSION__,
description="Google Cloud Log Formatter for Flask",
url="http://github.com/rai200890/flask_google_cloud_logger",
author="Raissa Ferreira",
author_email="[email protected]",
license="MIT",
packages=["flask_google_cloud_logger"],
install_requires=[
"google_cloud_logger>=0.0.2",
"google_cloud_logger>=0.1.0",
"flask>=1.0",
],
classifiers=[
Expand Down
54 changes: 17 additions & 37 deletions test/test_flask_google_cloud_formatter.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,18 @@
import pytest

from flask_google_cloud_logger.formatter import FlaskGoogleCloudFormatter
from flask_google_cloud_logger import FlaskGoogleCloudFormatter


@pytest.fixture
def formatter():
return FlaskGoogleCloudFormatter("")


@pytest.fixture
def record(mocker):
return mocker.Mock(
asctime="2018-08-30 20:40:57,245",
filename="_internal.py",
funcName="_log",
lineno="88",
levelname="WARNING",
getMessage=lambda: "farofa")


@pytest.fixture
def message_dict():
return {"extra": "extra_args", "extra_2": 1}


def test_make_metadata_when_request_context_isnt_available(
formatter, record, mocker):
assert formatter.make_metadata(record) == {}
def test_make_labels_when_request_context_isnt_available(formatter, mocker):
assert formatter.make_labels() == {}


def test_make_metadata_when_request_context_is_available(
formatter, record, mocker):
def test_make_metadata_when_request_context_is_available(formatter, mocker):
mocker.patch(
"flask_google_cloud_logger.formatter.has_request_context",
return_value=True)
Expand All @@ -50,21 +32,19 @@ def test_make_metadata_when_request_context_is_available(
g_mock.status_code = 200
g_mock.request_time = 0.01
g_mock.response = mocker.Mock(status_code=200)
metadata = formatter.make_metadata(record)
metadata = formatter.make_labels()

assert metadata == {
"userLabels": {
"client": {
"ip": "0.0.0.0",
"user_agent": "Browser",
"version": "1"
},
"connection": {
"method": "GET",
"path": "/",
"request_id": "4353658",
"status": 200
},
"latency": 0.01
}
"client": {
"ip": "0.0.0.0",
"user_agent": "Browser",
"version": "1"
},
"connection": {
"method": "GET",
"path": "/",
"request_id": "4353658",
"status": 200
},
"latency": 0.01
}

0 comments on commit 1f19c31

Please sign in to comment.