Skip to content

Commit 8f0bbf5

Browse files
author
Lars Solberg
committed
Replaced how logging is done, now using loguru
1 parent c769834 commit 8f0bbf5

11 files changed

Lines changed: 144 additions & 56 deletions

File tree

api/build/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ fastapi
1616
uvicorn
1717
dynaconf[all]
1818
aiofiles # To serve static files
19+
loguru # Log handler
1920

2021
#
2122
# Libs

api/data/opa/__init__.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
1+
import sys
12
import json
2-
import logging
3+
34
from typing import Dict
45
from dynaconf import LazySettings
56

7+
from opa.core.logger import log
8+
69
config = LazySettings()
710

11+
from opa.core.plugin import (
12+
HookDefinition,
13+
Hook,
14+
Driver,
15+
Setup,
16+
get_plugin_manager,
17+
get_component,
18+
get_instance,
19+
get_router,
20+
call_hook,
21+
call_hook_async,
22+
)
23+
824

925
def init_configuration():
1026
config.configure(
@@ -23,22 +39,9 @@ def init_configuration():
2339
],
2440
)
2541

26-
logging.debug(f'Using configuration environemnt={config.ENV_FOR_DYNACONF}')
27-
logging.debug('Configuration is:')
28-
logging.debug(json.dumps(config.as_dict(internal=False), indent=2))
42+
log.debug(f'Using configuration environemnt={config.ENV_FOR_DYNACONF}')
43+
log.debug('Configuration is:')
44+
log.debug(json.dumps(config.as_dict(internal=False), indent=2))
2945

3046

3147
state: Dict = {}
32-
33-
from opa.core.plugin import (
34-
HookDefinition,
35-
Hook,
36-
Driver,
37-
Setup,
38-
get_plugin_manager,
39-
get_component,
40-
get_instance,
41-
get_router,
42-
call_hook,
43-
call_hook_async,
44-
)

api/data/opa/core/logger.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import sys
2+
import logging
3+
import functools
4+
import loguru
5+
6+
7+
def log_func(*, entry=True, exit=True, level="DEBUG"):
8+
def wrapper(func):
9+
name = func.__name__
10+
11+
@functools.wraps(func)
12+
def wrapped(*args, **kwargs):
13+
logger = loguru.logger.opt(depth=1)
14+
if entry:
15+
logger.log(
16+
level, "Entering '{}' (args={}, kwargs={})", name, args, kwargs
17+
)
18+
result = func(*args, **kwargs)
19+
if exit:
20+
logger.log(level, "Exiting '{}' (result={})", name, result)
21+
return result
22+
23+
return wrapped
24+
25+
return wrapper
26+
27+
28+
class InterceptHandler(logging.Handler):
29+
stream = sys.stderr
30+
31+
def emit(self, record):
32+
# Get corresponding Loguru level if it exists
33+
try:
34+
level = loguru.logger.level(record.levelname).name
35+
except ValueError:
36+
level = record.levelno
37+
38+
# Find caller from where originated the logged message
39+
frame, depth = logging.currentframe(), 2
40+
while frame.f_code.co_filename == logging.__file__:
41+
frame = frame.f_back
42+
depth += 1
43+
44+
loguru.logger.opt(depth=depth, exception=record.exc_info).log(
45+
level, record.getMessage()
46+
)
47+
48+
49+
def get_loglevel():
50+
# Level from --log-level when using uvicorn
51+
# If uvicorn doesnt define a level, the logger default (root logger) is
52+
# used, which is WARNING
53+
return logging.getLogger('uvicorn').getEffectiveLevel()
54+
55+
56+
def get_logformat(record):
57+
if 'var' in record['extra']:
58+
return loguru._defaults.LOGURU_FORMAT + ' {extra[var]}\n'
59+
return loguru._defaults.LOGURU_FORMAT + '\n'
60+
61+
62+
class LogHandler:
63+
def __ror__(self, obj):
64+
if isinstance(obj, dict):
65+
logger = loguru.logger.bind(var=obj)
66+
else:
67+
logger = loguru.logger.bind(
68+
var={'str': str(obj), '__repr__': repr(obj), 'type': type(obj)}
69+
)
70+
71+
logger.opt(depth=1, ansi=True).debug('<yellow>Object</yellow>')
72+
return obj
73+
74+
def __call__(self, *args, **kwargs):
75+
return getattr(loguru.logger.opt(depth=1), self.level)(*args, **kwargs)
76+
77+
def __getattr__(self, level):
78+
handler = LogHandler()
79+
handler.level = level
80+
return handler
81+
82+
def setup(self):
83+
loguru.logger.remove()
84+
loguru.logger.add(sys.stdout, format=get_logformat, level=get_loglevel())
85+
loguru.logger.bind(request_id='app')
86+
logging.basicConfig(handlers=[InterceptHandler()], level=0)
87+
88+
# Can be removed when https://github.com/encode/uvicorn/issues/630 is fixed?
89+
# Also see https://github.com/Delgan/loguru/issues/247
90+
logging.getLogger().handlers = [InterceptHandler()]
91+
92+
93+
log = LogHandler()
94+
log.setup()

api/data/opa/core/plugin.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import json
55
import inspect
66
import asyncio
7-
import logging
87
import pkgutil
98
from collections import defaultdict
109
from importlib import import_module
@@ -13,7 +12,7 @@
1312
from fastapi import FastAPI, APIRouter
1413

1514
from opa.utils import unique, filter_dict_to_function
16-
from opa import config
15+
from opa import config, log
1716

1817

1918
class BasePlugin:
@@ -146,12 +145,13 @@ def register_driver(self, driver: Driver):
146145
raise Exception(
147146
f'Driver with this name ({name}) already exists. CaSe of driver is ignored.'
148147
)
149-
logging.debug(f'Registered driver {name}')
148+
log.debug(f'Registered driver {name}')
150149
self.drivers[name] = driver
151150

152151
def _preload_drivers(self):
153152
for name, values in config.OPTIONAL_COMPONENTS.items():
154-
name = name.lower()
153+
name = name.lower() | log
154+
{'test': 123} | log
155155
load = values.get('LOAD', 'auto')
156156
if load == 'no':
157157
continue
@@ -168,7 +168,7 @@ def _preload_drivers(self):
168168
driverinstance.pm = self
169169
self.optional_components[name] = driverinstance
170170

171-
logging.info(
171+
log.info(
172172
f'Connecting to {name} with driver {drivername}, using {driverinstance.opts}'
173173
)
174174
yield driverinstance
@@ -183,7 +183,7 @@ async def load_components(self):
183183
def load_sync_components_global(self):
184184
for driverinstance in self._preload_drivers():
185185
if asyncio.iscoroutinefunction(driverinstance.connect):
186-
logging.debug(f'Driver {driverinstance.name} is async, wont load')
186+
log.debug(f'Driver {driverinstance.name} is async, wont load')
187187
else:
188188
driverinstance.initialize()
189189

@@ -273,7 +273,7 @@ def _get_plugindata():
273273
else config.PLUGIN_PATHS
274274
) + ['/data/opa/plugins']
275275

276-
logging.info(
276+
log.info(
277277
'Plugin loading settings:'
278278
f' plugin-paths: {PLUGIN_PATHS}\n'
279279
f' whitelist-regex: {PLUGIN_WHITELIST_RE}\n'
@@ -303,16 +303,16 @@ def _get_plugindata():
303303
else:
304304
metafile = f'{allow_match}-meta.json'
305305

306-
logging.debug('')
307-
logging.debug(f'Checking if we should load "{allow_match}"')
306+
log.debug('')
307+
log.debug(f'Checking if we should load "{allow_match}"')
308308

309309
if os.path.exists(metafile):
310-
logging.debug(f'Found metafile @ {metafile}')
310+
log.debug(f'Found metafile @ {metafile}')
311311
metadata = json.load(open(metafile, 'r'))
312312
else:
313-
logging.debug(f'Metafile @ {metafile} does not exist, using empty metadata')
313+
log.debug(f'Metafile @ {metafile} does not exist, using empty metadata')
314314
metadata = {}
315-
logging.debug(f'Metadata: {metadata}')
315+
log.debug(f'Metadata: {metadata}')
316316

317317
load_checks = {}
318318

@@ -347,12 +347,12 @@ def _get_plugindata():
347347
)
348348

349349
load = all(load_checks.values())
350-
logging.debug(f'Load-checks: {load_checks}, overall({load})')
350+
log.debug(f'Load-checks: {load_checks}, overall({load})')
351351

352352
if not load:
353353
continue
354354

355-
logging.info(f'Loading plugin: {plugin.name}')
355+
log.info(f'Loading plugin: {plugin.name}')
356356

357357
"""
358358
We should consider using lazy-loading instead of import_module.

api/data/opa/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from starlette.middleware.cors import CORSMiddleware
66
from fastapi import Depends, FastAPI, Header, HTTPException
77

8-
from opa import config, init_configuration, state
98
from opa.core import plugin
9+
from opa import config, init_configuration, state
1010

1111
app: FastAPI
1212

api/data/opa/plugins/driver_celery.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import logging
2-
31
from celery import Celery
42

53
from opa import Driver, Hook, Setup, HookDefinition, call_hook

api/data/opa/plugins/driver_mongodb.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import logging
2-
31
from motor.motor_asyncio import AsyncIOMotorClient
42

5-
from opa import Driver
3+
from opa import Driver, log
64
from opa.utils import host_exists
75

86

@@ -23,7 +21,7 @@ async def connect(self):
2321

2422
# This throws an exception if not connected
2523
info = await self.instance.server_info()
26-
logging.debug(info)
24+
log.debug(info)
2725

2826
async def disconnect(self):
2927
self.instance.close()

api/data/opa/plugins/driver_redis.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import logging
21
import aioredis as aioredislib
32
import walrus as walruslib
43

api/data/opa/utils/mongodb.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import logging
2-
31
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
42

5-
from opa import config
3+
from opa import config, log
64

75

86
class DataBase:
@@ -13,7 +11,7 @@ class DataBase:
1311

1412

1513
async def connect_to_mongo():
16-
logging.info("Connecting to mongodb database..")
14+
log.info("Connecting to mongodb database..")
1715
db.client = AsyncIOMotorClient(
1816
config.OPTIONAL_COMPONENTS.MONGODB.URL,
1917
socketTimeoutMS=1000,
@@ -23,11 +21,11 @@ async def connect_to_mongo():
2321

2422
# This throws an exception if not connected
2523
info = await db.client.server_info()
26-
logging.debug(info)
24+
log.debug(info)
2725

2826

2927
async def close_mongo_connection():
30-
logging.info("Closing mongodb connection")
28+
log.info("Closing mongodb connection")
3129
db.client.close()
3230

3331

api/data/opa/utils/redis.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import logging
21
import aioredis as aioredislib
32
import walrus as walruslib
43

5-
from opa import config
4+
from opa import config, log
65

76

87
class Redis:
@@ -14,7 +13,7 @@ class Redis:
1413

1514

1615
async def connect_to_aioredis():
17-
logging.info("Connectiong to redis using aioredis")
16+
log.info("Connectiong to redis using aioredis")
1817
redis.aioredis = await aioredislib.create_redis_pool(
1918
config.OPTIONAL_COMPONENTS.REDIS.URL
2019
)
@@ -30,7 +29,7 @@ async def get_aioredis():
3029

3130

3231
async def connect_to_walrus():
33-
logging.info("Connectiong to redis using walrus")
32+
log.info("Connectiong to redis using walrus")
3433
redis.walrus = walruslib.Database.from_url(config.OPTIONAL_COMPONENTS.REDIS.URL)
3534

3635
# Raises exception if not able to connect

0 commit comments

Comments
 (0)