koi-net 1.1.0b8__py3-none-any.whl → 1.2.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- koi_net/__init__.py +2 -1
- koi_net/assembler.py +82 -0
- koi_net/cli/__init__.py +1 -0
- koi_net/cli/commands.py +99 -0
- koi_net/cli/models.py +41 -0
- koi_net/config.py +34 -0
- koi_net/context.py +11 -28
- koi_net/core.py +63 -179
- koi_net/default_actions.py +10 -1
- koi_net/effector.py +61 -34
- koi_net/handshaker.py +39 -0
- koi_net/identity.py +2 -3
- koi_net/interfaces/entrypoint.py +5 -0
- koi_net/interfaces/worker.py +17 -0
- koi_net/lifecycle.py +85 -48
- koi_net/logger.py +176 -0
- koi_net/network/error_handler.py +18 -16
- koi_net/network/event_queue.py +17 -185
- koi_net/network/graph.py +15 -10
- koi_net/network/poll_event_buffer.py +26 -0
- koi_net/network/request_handler.py +54 -47
- koi_net/network/resolver.py +18 -21
- koi_net/network/response_handler.py +79 -15
- koi_net/poller.py +18 -9
- koi_net/processor/event_worker.py +117 -0
- koi_net/processor/handler.py +4 -2
- koi_net/processor/{default_handlers.py → handlers.py} +109 -59
- koi_net/processor/knowledge_object.py +19 -7
- koi_net/processor/kobj_queue.py +51 -0
- koi_net/processor/kobj_worker.py +44 -0
- koi_net/processor/{knowledge_pipeline.py → pipeline.py} +31 -53
- koi_net/protocol/api_models.py +7 -3
- koi_net/protocol/envelope.py +5 -6
- koi_net/protocol/model_map.py +61 -0
- koi_net/protocol/node.py +3 -3
- koi_net/protocol/secure.py +8 -8
- koi_net/secure.py +33 -13
- koi_net/sentry.py +13 -0
- koi_net/server.py +44 -78
- koi_net/utils.py +18 -0
- {koi_net-1.1.0b8.dist-info → koi_net-1.2.0b2.dist-info}/METADATA +8 -3
- koi_net-1.2.0b2.dist-info/RECORD +52 -0
- koi_net-1.2.0b2.dist-info/entry_points.txt +2 -0
- koi_net/actor.py +0 -60
- koi_net/processor/interface.py +0 -101
- koi_net-1.1.0b8.dist-info/RECORD +0 -38
- {koi_net-1.1.0b8.dist-info → koi_net-1.2.0b2.dist-info}/WHEEL +0 -0
- {koi_net-1.1.0b8.dist-info → koi_net-1.2.0b2.dist-info}/licenses/LICENSE +0 -0
koi_net/effector.py
CHANGED
|
@@ -1,33 +1,43 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from typing import Callable
|
|
3
3
|
from enum import StrEnum
|
|
4
4
|
from rid_lib.ext import Cache, Bundle
|
|
5
5
|
from rid_lib.core import RID, RIDType
|
|
6
6
|
from rid_lib.types import KoiNetNode
|
|
7
|
+
from .network.resolver import NetworkResolver
|
|
8
|
+
from .processor.kobj_queue import KobjQueue
|
|
9
|
+
from .identity import NodeIdentity
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
log = structlog.stdlib.get_logger()
|
|
9
12
|
|
|
10
|
-
if TYPE_CHECKING:
|
|
11
|
-
from .network.resolver import NetworkResolver
|
|
12
|
-
from .processor.interface import ProcessorInterface
|
|
13
|
-
from .context import ActionContext
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
class ActionContext:
|
|
15
|
+
"""Provides action handlers access to other subsystems."""
|
|
16
|
+
|
|
17
|
+
identity: NodeIdentity
|
|
16
18
|
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
identity: NodeIdentity,
|
|
22
|
+
):
|
|
23
|
+
self.identity = identity
|
|
24
|
+
|
|
17
25
|
|
|
18
26
|
class BundleSource(StrEnum):
|
|
19
27
|
CACHE = "CACHE"
|
|
20
28
|
ACTION = "ACTION"
|
|
21
29
|
|
|
22
30
|
class Effector:
|
|
31
|
+
"""Subsystem for dereferencing RIDs."""
|
|
32
|
+
|
|
23
33
|
cache: Cache
|
|
24
|
-
resolver:
|
|
25
|
-
|
|
26
|
-
action_context:
|
|
34
|
+
resolver: NetworkResolver
|
|
35
|
+
kobj_queue: KobjQueue | None
|
|
36
|
+
action_context: ActionContext | None
|
|
27
37
|
_action_table: dict[
|
|
28
38
|
type[RID],
|
|
29
39
|
Callable[
|
|
30
|
-
[
|
|
40
|
+
[ActionContext, RID],
|
|
31
41
|
Bundle | None
|
|
32
42
|
]
|
|
33
43
|
] = dict()
|
|
@@ -35,22 +45,16 @@ class Effector:
|
|
|
35
45
|
def __init__(
|
|
36
46
|
self,
|
|
37
47
|
cache: Cache,
|
|
48
|
+
resolver: NetworkResolver,
|
|
49
|
+
kobj_queue: KobjQueue,
|
|
50
|
+
identity: NodeIdentity
|
|
38
51
|
):
|
|
39
52
|
self.cache = cache
|
|
40
|
-
self.resolver = None
|
|
41
|
-
self.processor = None
|
|
42
|
-
self.action_context = None
|
|
43
|
-
self._action_table = self.__class__._action_table.copy()
|
|
44
|
-
|
|
45
|
-
def set_processor(self, processor: "ProcessorInterface"):
|
|
46
|
-
self.processor = processor
|
|
47
|
-
|
|
48
|
-
def set_resolver(self, resolver: "NetworkResolver"):
|
|
49
53
|
self.resolver = resolver
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
self.
|
|
53
|
-
|
|
54
|
+
self.kobj_queue = kobj_queue
|
|
55
|
+
self.action_context = ActionContext(identity)
|
|
56
|
+
self._action_table = self.__class__._action_table.copy()
|
|
57
|
+
|
|
54
58
|
@classmethod
|
|
55
59
|
def register_default_action(cls, rid_type: RIDType):
|
|
56
60
|
def decorator(func: Callable) -> Callable:
|
|
@@ -59,6 +63,16 @@ class Effector:
|
|
|
59
63
|
return decorator
|
|
60
64
|
|
|
61
65
|
def register_action(self, rid_type: RIDType):
|
|
66
|
+
"""Registers a new dereference action for an RID type.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
This function should be used as a decorator on an action function::
|
|
70
|
+
|
|
71
|
+
@node.register_action(KoiNetNode)
|
|
72
|
+
def deref_koi_net_node(ctx: ActionContext, rid: KoiNetNode):
|
|
73
|
+
# return a Bundle or None
|
|
74
|
+
return
|
|
75
|
+
"""
|
|
62
76
|
def decorator(func: Callable) -> Callable:
|
|
63
77
|
self._action_table[rid_type] = func
|
|
64
78
|
return func
|
|
@@ -68,18 +82,18 @@ class Effector:
|
|
|
68
82
|
bundle = self.cache.read(rid)
|
|
69
83
|
|
|
70
84
|
if bundle:
|
|
71
|
-
|
|
85
|
+
log.debug("Cache hit")
|
|
72
86
|
return bundle, BundleSource.CACHE
|
|
73
87
|
else:
|
|
74
|
-
|
|
88
|
+
log.debug("Cache miss")
|
|
75
89
|
return None
|
|
76
90
|
|
|
77
91
|
def _try_action(self, rid: RID) -> tuple[Bundle, BundleSource] | None:
|
|
78
92
|
if type(rid) not in self._action_table:
|
|
79
|
-
|
|
93
|
+
log.debug("No action available")
|
|
80
94
|
return None
|
|
81
95
|
|
|
82
|
-
|
|
96
|
+
log.debug("Action available")
|
|
83
97
|
func = self._action_table[type(rid)]
|
|
84
98
|
bundle = func(
|
|
85
99
|
ctx=self.action_context,
|
|
@@ -87,10 +101,10 @@ class Effector:
|
|
|
87
101
|
)
|
|
88
102
|
|
|
89
103
|
if bundle:
|
|
90
|
-
|
|
104
|
+
log.debug("Action hit")
|
|
91
105
|
return bundle, BundleSource.ACTION
|
|
92
106
|
else:
|
|
93
|
-
|
|
107
|
+
log.debug("Action miss")
|
|
94
108
|
return None
|
|
95
109
|
|
|
96
110
|
|
|
@@ -98,10 +112,10 @@ class Effector:
|
|
|
98
112
|
bundle, source = self.resolver.fetch_remote_bundle(rid)
|
|
99
113
|
|
|
100
114
|
if bundle:
|
|
101
|
-
|
|
115
|
+
log.debug("Network hit")
|
|
102
116
|
return bundle, source
|
|
103
117
|
else:
|
|
104
|
-
|
|
118
|
+
log.debug("Network miss")
|
|
105
119
|
return None
|
|
106
120
|
|
|
107
121
|
|
|
@@ -112,7 +126,20 @@ class Effector:
|
|
|
112
126
|
use_network: bool = False,
|
|
113
127
|
handle_result: bool = True
|
|
114
128
|
) -> Bundle | None:
|
|
115
|
-
|
|
129
|
+
"""Dereferences an RID.
|
|
130
|
+
|
|
131
|
+
Attempts to dereference an RID by (in order) reading the cache,
|
|
132
|
+
calling a bound action, or fetching from other nodes in the
|
|
133
|
+
newtork.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
rid: RID to dereference
|
|
137
|
+
refresh_cache: skips cache read when `True`
|
|
138
|
+
use_network: enables fetching from other nodes when `True`
|
|
139
|
+
handle_result: handles resulting bundle with knowledge pipeline when `True`
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
log.debug(f"Dereferencing {rid!r}")
|
|
116
143
|
|
|
117
144
|
bundle, source = (
|
|
118
145
|
# if `refresh_cache`, skip try cache
|
|
@@ -128,7 +155,7 @@ class Effector:
|
|
|
128
155
|
and bundle is not None
|
|
129
156
|
and source != BundleSource.CACHE
|
|
130
157
|
):
|
|
131
|
-
self.
|
|
158
|
+
self.kobj_queue.put_kobj(
|
|
132
159
|
bundle=bundle,
|
|
133
160
|
source=source if type(source) is KoiNetNode else None
|
|
134
161
|
)
|
koi_net/handshaker.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import structlog
|
|
2
|
+
from rid_lib.ext import Cache
|
|
3
|
+
from rid_lib.types import KoiNetNode
|
|
4
|
+
from koi_net.identity import NodeIdentity
|
|
5
|
+
from koi_net.network.event_queue import EventQueue
|
|
6
|
+
from .protocol.event import Event, EventType
|
|
7
|
+
|
|
8
|
+
log = structlog.stdlib.get_logger()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Handshaker:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
cache: Cache,
|
|
15
|
+
identity: NodeIdentity,
|
|
16
|
+
event_queue: EventQueue
|
|
17
|
+
):
|
|
18
|
+
self.cache = cache
|
|
19
|
+
self.identity = identity
|
|
20
|
+
self.event_queue = event_queue
|
|
21
|
+
|
|
22
|
+
def handshake_with(self, target: KoiNetNode):
|
|
23
|
+
"""Initiates a handshake with target node.
|
|
24
|
+
Pushes successive `FORGET` and `NEW` events to target node to
|
|
25
|
+
reset the target's cache in case it already knew this node.
|
|
26
|
+
"""
|
|
27
|
+
log.debug(f"Initiating handshake with {target}")
|
|
28
|
+
self.event_queue.push_event_to(
|
|
29
|
+
Event.from_rid(
|
|
30
|
+
event_type=EventType.FORGET,
|
|
31
|
+
rid=self.identity.rid),
|
|
32
|
+
target=target
|
|
33
|
+
)
|
|
34
|
+
self.event_queue.push_event_to(
|
|
35
|
+
event=Event.from_bundle(
|
|
36
|
+
event_type=EventType.NEW,
|
|
37
|
+
bundle=self.cache.read(self.identity.rid)),
|
|
38
|
+
target=target
|
|
39
|
+
)
|
koi_net/identity.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from rid_lib.types.koi_net_node import KoiNetNode
|
|
3
3
|
from .config import NodeConfig
|
|
4
4
|
from .protocol.node import NodeProfile
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
logger = logging.getLogger(__name__)
|
|
6
|
+
log = structlog.stdlib.get_logger()
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class NodeIdentity:
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class End:
|
|
5
|
+
"""Class for a sentinel value by knowledge handlers."""
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
STOP_WORKER = End()
|
|
9
|
+
|
|
10
|
+
class ThreadWorker:
|
|
11
|
+
thread: threading.Thread
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.thread = threading.Thread(target=self.run)
|
|
15
|
+
|
|
16
|
+
def run(self):
|
|
17
|
+
...
|
koi_net/lifecycle.py
CHANGED
|
@@ -1,104 +1,141 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from contextlib import contextmanager, asynccontextmanager
|
|
3
3
|
|
|
4
|
+
from rid_lib.ext import Bundle, Cache
|
|
4
5
|
from rid_lib.types import KoiNetNode
|
|
5
6
|
|
|
6
|
-
from .
|
|
7
|
-
from .
|
|
7
|
+
from .handshaker import Handshaker
|
|
8
|
+
from .network.request_handler import RequestHandler
|
|
9
|
+
from .processor.kobj_worker import KnowledgeProcessingWorker
|
|
10
|
+
from .network.event_queue import EventQueue
|
|
11
|
+
from .processor.event_worker import EventProcessingWorker
|
|
12
|
+
from .protocol.api_models import ErrorResponse
|
|
13
|
+
from .interfaces.worker import STOP_WORKER
|
|
8
14
|
from .config import NodeConfig
|
|
9
|
-
from .processor.
|
|
15
|
+
from .processor.kobj_queue import KobjQueue
|
|
10
16
|
from .network.graph import NetworkGraph
|
|
11
17
|
from .identity import NodeIdentity
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
log = structlog.stdlib.get_logger()
|
|
14
20
|
|
|
15
21
|
|
|
16
22
|
class NodeLifecycle:
|
|
23
|
+
"""Manages node startup and shutdown processes."""
|
|
24
|
+
|
|
17
25
|
config: NodeConfig
|
|
26
|
+
identity: NodeIdentity
|
|
18
27
|
graph: NetworkGraph
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
kobj_queue: KobjQueue
|
|
29
|
+
kobj_worker: KnowledgeProcessingWorker
|
|
30
|
+
event_queue: EventQueue
|
|
31
|
+
event_worker: EventProcessingWorker
|
|
32
|
+
cache: Cache
|
|
33
|
+
handshaker: Handshaker
|
|
34
|
+
request_handler: RequestHandler
|
|
22
35
|
|
|
23
36
|
def __init__(
|
|
24
37
|
self,
|
|
25
38
|
config: NodeConfig,
|
|
26
39
|
identity: NodeIdentity,
|
|
27
40
|
graph: NetworkGraph,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
kobj_queue: KobjQueue,
|
|
42
|
+
kobj_worker: KnowledgeProcessingWorker,
|
|
43
|
+
event_queue: EventQueue,
|
|
44
|
+
event_worker: EventProcessingWorker,
|
|
45
|
+
cache: Cache,
|
|
46
|
+
handshaker: Handshaker,
|
|
47
|
+
request_handler: RequestHandler
|
|
32
48
|
):
|
|
33
49
|
self.config = config
|
|
34
50
|
self.identity = identity
|
|
35
51
|
self.graph = graph
|
|
36
|
-
self.
|
|
37
|
-
self.
|
|
38
|
-
self.
|
|
39
|
-
self.
|
|
52
|
+
self.kobj_queue = kobj_queue
|
|
53
|
+
self.kobj_worker = kobj_worker
|
|
54
|
+
self.event_queue = event_queue
|
|
55
|
+
self.event_worker = event_worker
|
|
56
|
+
self.cache = cache
|
|
57
|
+
self.handshaker = handshaker
|
|
58
|
+
self.request_handler = request_handler
|
|
40
59
|
|
|
41
60
|
@contextmanager
|
|
42
61
|
def run(self):
|
|
62
|
+
"""Synchronous context manager for node startup and shutdown."""
|
|
43
63
|
try:
|
|
44
|
-
|
|
64
|
+
log.info("Starting node lifecycle...")
|
|
45
65
|
self.start()
|
|
46
66
|
yield
|
|
47
67
|
except KeyboardInterrupt:
|
|
48
|
-
|
|
68
|
+
log.info("Keyboard interrupt!")
|
|
49
69
|
finally:
|
|
50
|
-
|
|
70
|
+
log.info("Stopping node lifecycle...")
|
|
51
71
|
self.stop()
|
|
52
72
|
|
|
53
73
|
@asynccontextmanager
|
|
54
74
|
async def async_run(self):
|
|
75
|
+
"""Asynchronous context manager for node startup and shutdown."""
|
|
55
76
|
try:
|
|
56
|
-
|
|
77
|
+
log.info("Starting async node lifecycle...")
|
|
57
78
|
self.start()
|
|
58
79
|
yield
|
|
59
80
|
except KeyboardInterrupt:
|
|
60
|
-
|
|
81
|
+
log.info("Keyboard interrupt!")
|
|
61
82
|
finally:
|
|
62
|
-
|
|
83
|
+
log.info("Stopping async node lifecycle...")
|
|
63
84
|
self.stop()
|
|
64
85
|
|
|
65
86
|
def start(self):
|
|
66
|
-
"""Starts a node
|
|
87
|
+
"""Starts a node.
|
|
67
88
|
|
|
68
|
-
Starts the processor thread (if enabled).
|
|
89
|
+
Starts the processor thread (if enabled). Generates network
|
|
90
|
+
graph from nodes and edges in cache. Processes any state changes
|
|
91
|
+
of node bundle. Initiates handshake with first contact if node
|
|
92
|
+
doesn't have any neighbors. Catches up with coordinator state.
|
|
69
93
|
"""
|
|
70
|
-
|
|
71
|
-
logger.info("Starting processor worker thread")
|
|
72
|
-
self.processor.worker_thread.start()
|
|
94
|
+
log.info("Starting processor worker thread")
|
|
73
95
|
|
|
96
|
+
self.kobj_worker.thread.start()
|
|
97
|
+
self.event_worker.thread.start()
|
|
74
98
|
self.graph.generate()
|
|
75
99
|
|
|
76
100
|
# refresh to reflect changes (if any) in config.yaml
|
|
77
|
-
self.effector.deref(self.identity.rid, refresh_cache=True)
|
|
78
101
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
self.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if not self.graph.get_neighbors() and self.config.koi_net.first_contact.rid:
|
|
87
|
-
logger.debug(f"I don't have any neighbors, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
|
|
88
|
-
|
|
89
|
-
self.actor.handshake_with(self.config.koi_net.first_contact.rid)
|
|
102
|
+
self.kobj_queue.put_kobj(bundle=Bundle.generate(
|
|
103
|
+
rid=self.identity.rid,
|
|
104
|
+
contents=self.identity.profile.model_dump()
|
|
105
|
+
))
|
|
106
|
+
|
|
107
|
+
log.debug("Waiting for kobj queue to empty")
|
|
108
|
+
self.kobj_queue.q.join()
|
|
90
109
|
|
|
91
|
-
|
|
92
|
-
|
|
110
|
+
coordinators = self.graph.get_neighbors(direction="in", allowed_type=KoiNetNode)
|
|
111
|
+
|
|
112
|
+
if len(coordinators) > 0:
|
|
113
|
+
for coordinator in coordinators:
|
|
114
|
+
payload = self.request_handler.fetch_manifests(
|
|
115
|
+
node=coordinator,
|
|
116
|
+
rid_types=[KoiNetNode]
|
|
117
|
+
)
|
|
118
|
+
if type(payload) is ErrorResponse:
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
for manifest in payload.manifests:
|
|
122
|
+
self.kobj_queue.put_kobj(
|
|
123
|
+
manifest=manifest,
|
|
124
|
+
source=coordinator
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
elif self.config.koi_net.first_contact.rid:
|
|
128
|
+
log.debug(f"I don't have any edges with coordinators, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
|
|
129
|
+
|
|
130
|
+
self.handshaker.handshake_with(self.config.koi_net.first_contact.rid)
|
|
93
131
|
|
|
94
132
|
|
|
95
133
|
def stop(self):
|
|
96
|
-
"""Stops a node
|
|
134
|
+
"""Stops a node.
|
|
97
135
|
|
|
98
|
-
Finishes processing knowledge object queue.
|
|
136
|
+
Finishes processing knowledge object queue.
|
|
99
137
|
"""
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
self.processor.flush_kobj_queue()
|
|
138
|
+
log.info(f"Waiting for kobj queue to empty ({self.kobj_queue.q.unfinished_tasks} tasks remaining)")
|
|
139
|
+
|
|
140
|
+
self.kobj_queue.q.put(STOP_WORKER)
|
|
141
|
+
self.event_queue.q.put(STOP_WORKER)
|
koi_net/logger.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import logging
|
|
3
|
+
from logging.handlers import RotatingFileHandler
|
|
4
|
+
import colorama
|
|
5
|
+
import structlog
|
|
6
|
+
import sys
|
|
7
|
+
# from sentry_sdk import logger as sentry_logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def my_processor(_, __, event: dict):
|
|
11
|
+
# print(_, __, event)
|
|
12
|
+
event["path"] = event["module"] + "." + event["func_name"]
|
|
13
|
+
return event
|
|
14
|
+
|
|
15
|
+
# def sentry_processor(_, method, event: dict):
|
|
16
|
+
# print(event)
|
|
17
|
+
# if method == "critical":
|
|
18
|
+
# sentry_logger.fatal(
|
|
19
|
+
# event["event"],
|
|
20
|
+
# attributes=event
|
|
21
|
+
# )
|
|
22
|
+
# elif method == "info":
|
|
23
|
+
# sentry_logger.info(
|
|
24
|
+
# event["event"],
|
|
25
|
+
# attributes=event
|
|
26
|
+
# )
|
|
27
|
+
# elif method == "debug":
|
|
28
|
+
# sentry_logger.debug(
|
|
29
|
+
# event["event"],
|
|
30
|
+
# attributes=event
|
|
31
|
+
# )
|
|
32
|
+
# return event
|
|
33
|
+
|
|
34
|
+
console_renderer = structlog.dev.ConsoleRenderer(
|
|
35
|
+
columns=[
|
|
36
|
+
# Render the timestamp without the key name in yellow.
|
|
37
|
+
structlog.dev.Column(
|
|
38
|
+
"timestamp",
|
|
39
|
+
structlog.dev.KeyValueColumnFormatter(
|
|
40
|
+
key_style=None,
|
|
41
|
+
value_style=colorama.Style.DIM,
|
|
42
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
43
|
+
value_repr=lambda t: datetime.fromisoformat(t).strftime("%Y-%m-%d %H:%M:%S"),
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
structlog.dev.Column(
|
|
47
|
+
"level",
|
|
48
|
+
structlog.dev.LogLevelColumnFormatter(
|
|
49
|
+
level_styles={
|
|
50
|
+
level: colorama.Style.BRIGHT + color
|
|
51
|
+
for level, color in {
|
|
52
|
+
"critical": colorama.Fore.RED,
|
|
53
|
+
"exception": colorama.Fore.RED,
|
|
54
|
+
"error": colorama.Fore.RED,
|
|
55
|
+
"warn": colorama.Fore.YELLOW,
|
|
56
|
+
"warning": colorama.Fore.YELLOW,
|
|
57
|
+
"info": colorama.Fore.GREEN,
|
|
58
|
+
"debug": colorama.Fore.GREEN,
|
|
59
|
+
"notset": colorama.Back.RED,
|
|
60
|
+
}.items()
|
|
61
|
+
},
|
|
62
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
63
|
+
width=9
|
|
64
|
+
)
|
|
65
|
+
),
|
|
66
|
+
# Render the event without the key name in bright magenta.
|
|
67
|
+
|
|
68
|
+
# Default formatter for all keys not explicitly mentioned. The key is
|
|
69
|
+
# cyan, the value is green.
|
|
70
|
+
structlog.dev.Column(
|
|
71
|
+
"path",
|
|
72
|
+
structlog.dev.KeyValueColumnFormatter(
|
|
73
|
+
key_style=None,
|
|
74
|
+
value_style=colorama.Fore.MAGENTA,
|
|
75
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
76
|
+
value_repr=str,
|
|
77
|
+
width=30
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
# structlog.dev.Column(
|
|
81
|
+
# "func_name",
|
|
82
|
+
# structlog.dev.KeyValueColumnFormatter(
|
|
83
|
+
# key_style=None,
|
|
84
|
+
# value_style=colorama.Fore.MAGENTA,
|
|
85
|
+
# reset_style=colorama.Style.RESET_ALL,
|
|
86
|
+
# value_repr=str,
|
|
87
|
+
# prefix="(",
|
|
88
|
+
# postfix=")",
|
|
89
|
+
# width=15
|
|
90
|
+
# ),
|
|
91
|
+
# ),
|
|
92
|
+
structlog.dev.Column(
|
|
93
|
+
"event",
|
|
94
|
+
structlog.dev.KeyValueColumnFormatter(
|
|
95
|
+
key_style=None,
|
|
96
|
+
value_style=colorama.Fore.WHITE,
|
|
97
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
98
|
+
value_repr=str,
|
|
99
|
+
width=30
|
|
100
|
+
),
|
|
101
|
+
),
|
|
102
|
+
structlog.dev.Column(
|
|
103
|
+
"",
|
|
104
|
+
structlog.dev.KeyValueColumnFormatter(
|
|
105
|
+
key_style=colorama.Fore.BLUE,
|
|
106
|
+
value_style=colorama.Fore.GREEN,
|
|
107
|
+
reset_style=colorama.Style.RESET_ALL,
|
|
108
|
+
value_repr=str,
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
]
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
structlog.configure(
|
|
115
|
+
processors=[
|
|
116
|
+
# If log level is too low, abort pipeline and throw away log entry.
|
|
117
|
+
structlog.stdlib.filter_by_level,
|
|
118
|
+
# Add the name of the logger to event dict.
|
|
119
|
+
structlog.stdlib.add_logger_name,
|
|
120
|
+
# Add log level to event dict.
|
|
121
|
+
structlog.stdlib.add_log_level,
|
|
122
|
+
# Perform %-style formatting.
|
|
123
|
+
structlog.stdlib.PositionalArgumentsFormatter(),
|
|
124
|
+
# Add a timestamp in ISO 8601 format.
|
|
125
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
|
126
|
+
# If the "stack_info" key in the event dict is true, remove it and
|
|
127
|
+
# render the current stack trace in the "stack" key.
|
|
128
|
+
structlog.processors.StackInfoRenderer(),
|
|
129
|
+
# If the "exc_info" key in the event dict is either true or a
|
|
130
|
+
# sys.exc_info() tuple, remove "exc_info" and render the exception
|
|
131
|
+
# with traceback into the "exception" key.
|
|
132
|
+
# structlog.processors.format_exc_info,
|
|
133
|
+
# If some value is in bytes, decode it to a Unicode str.
|
|
134
|
+
structlog.processors.UnicodeDecoder(),
|
|
135
|
+
# Add callsite parameters.
|
|
136
|
+
structlog.processors.CallsiteParameterAdder(
|
|
137
|
+
{
|
|
138
|
+
structlog.processors.CallsiteParameter.MODULE,
|
|
139
|
+
structlog.processors.CallsiteParameter.FUNC_NAME,
|
|
140
|
+
# structlog.processors.CallsiteParameter.LINENO,
|
|
141
|
+
}
|
|
142
|
+
),
|
|
143
|
+
my_processor,
|
|
144
|
+
# Render the final event dict as JSON.
|
|
145
|
+
# sentry_processor,
|
|
146
|
+
console_renderer
|
|
147
|
+
# structlog.processors.JSONRenderer()
|
|
148
|
+
|
|
149
|
+
],
|
|
150
|
+
# `wrapper_class` is the bound logger that you get back from
|
|
151
|
+
# get_logger(). This one imitates the API of `logging.Logger`.
|
|
152
|
+
wrapper_class=structlog.stdlib.BoundLogger,
|
|
153
|
+
# `logger_factory` is used to create wrapped loggers that are used for
|
|
154
|
+
# OUTPUT. This one returns a `logging.Logger`. The final value (a JSON
|
|
155
|
+
# string) from the final processor (`JSONRenderer`) will be passed to
|
|
156
|
+
# the method of the same name as that you've called on the bound logger.
|
|
157
|
+
logger_factory=structlog.stdlib.LoggerFactory(),
|
|
158
|
+
# Effectively freeze configuration after creating the first bound
|
|
159
|
+
# logger.
|
|
160
|
+
cache_logger_on_first_use=True,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
file_handler = RotatingFileHandler(
|
|
164
|
+
filename="app.log",
|
|
165
|
+
maxBytes=10 * 1024 * 1024,
|
|
166
|
+
backupCount=5,
|
|
167
|
+
encoding="utf-8"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
logging.basicConfig(
|
|
171
|
+
format="%(message)s",
|
|
172
|
+
stream=sys.stdout,
|
|
173
|
+
level=logging.INFO,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# log = structlog.stdlib.get_logger()
|