1515
1616import logging
1717import threading
18- from abc import abstractmethod
18+ from abc import ABC , abstractmethod
1919from datetime import datetime
2020from typing import Any , Dict , List , Optional , Type , cast
2121
@@ -31,7 +31,73 @@ class BaseLogStoreConfig(StackComponentConfig):
3131 """Base configuration for all log stores."""
3232
3333
34- class BaseLogStore (StackComponent ):
34+ class BaseLogStoreEmitter :
35+ """Base class for all ZenML log store emitters.
36+
37+ The emitter is the entry point for all log records to be emitted to the log
38+ store. The process of emitting a log record is as follows:
39+
40+ 1. instantiate a BaseLogStoreEmitter
41+ 2. create an emitter by calling log_store.register_emitter() and passing the
42+ log model and optional metadata to be attached to each log record
43+ 3. emit the log record by calling emitter.emit() and passing the log record
44+ 4. deregister the emitter when all logs have been emitted by calling
45+ emitter.deregister()
46+ """
47+
48+ def __init__ (
49+ self ,
50+ name : str ,
51+ log_store : "BaseLogStore" ,
52+ log_model : LogsResponse ,
53+ metadata : Dict [str , Any ],
54+ ) -> None :
55+ """Initialize a log store emitter.
56+
57+ Args:
58+ name: The name of the emitter.
59+ log_store: The log store to emit logs to.
60+ log_model: The log model associated with the emitter.
61+ metadata: Additional metadata to attach to all log entries that will
62+ be emitted by this emitter.
63+ """
64+ self ._name = name
65+ self ._log_store = log_store
66+ self ._log_model = log_model
67+ self ._metadata = metadata
68+
69+ @property
70+ def name (self ) -> str :
71+ """The name of the emitter.
72+
73+ Returns:
74+ The name of the emitter.
75+ """
76+ return self ._name
77+
78+ @property
79+ def log_model (self ) -> LogsResponse :
80+ """The log model associated with the emitter.
81+
82+ Returns:
83+ The log model.
84+ """
85+ return self ._log_model
86+
87+ def emit (self , record : logging .LogRecord ) -> None :
88+ """Emit a log record to the log store.
89+
90+ Args:
91+ record: The log record to emit.
92+ """
93+ self ._log_store ._emit (self , record , metadata = self ._metadata )
94+
95+ def deregister (self ) -> None :
96+ """Deregister the emitter from the log store."""
97+ self ._log_store .deregister_emitter (self )
98+
99+
100+ class BaseLogStore (StackComponent , ABC ):
35101 """Base class for all ZenML log stores.
36102
37103 A log store is responsible for collecting, storing, and retrieving logs
@@ -47,7 +113,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
47113 **kwargs: Keyword arguments for the base class.
48114 """
49115 super ().__init__ (* args , ** kwargs )
50- self ._emitter_counter = 0
116+ self ._emitters : Dict [ str , BaseLogStoreEmitter ] = {}
51117 self ._lock = threading .RLock ()
52118
53119 @property
@@ -59,56 +125,88 @@ def config(self) -> BaseLogStoreConfig:
59125 """
60126 return cast (BaseLogStoreConfig , self ._config )
61127
128+ @property
129+ def emitter_class (self ) -> Type [BaseLogStoreEmitter ]:
130+ """Class of the emitter.
131+
132+ Returns:
133+ The class of the emitter.
134+ """
135+ return BaseLogStoreEmitter
136+
137+ def register_emitter (
138+ self , name : str , log_model : LogsResponse , metadata : Dict [str , Any ]
139+ ) -> BaseLogStoreEmitter :
140+ """Register an emitter for the log store.
141+
142+ Args:
143+ name: The name of the emitter.
144+ log_model: The log model associated with the emitter.
145+ metadata: Additional metadata to attach to the log entry.
146+
147+ Returns:
148+ The emitter.
149+ """
150+ with self ._lock :
151+ emitter = self .emitter_class (name , self , log_model , metadata )
152+ self ._emitters [name ] = emitter
153+ return emitter
154+
155+ def deregister_emitter (self , emitter : BaseLogStoreEmitter ) -> None :
156+ """Deregister an emitter registered with the log store.
157+
158+ Args:
159+ emitter: The emitter to deregister.
160+ """
161+ with self ._lock :
162+ if emitter .name not in self ._emitters :
163+ return
164+ self ._finalize (emitter )
165+ del self ._emitters [emitter .name ]
166+ if len (self ._emitters ) == 0 :
167+ self .flush (blocking = False )
168+
62169 @abstractmethod
63- def emit (
170+ def _emit (
64171 self ,
172+ emitter : BaseLogStoreEmitter ,
65173 record : logging .LogRecord ,
66- log_model : LogsResponse ,
67174 metadata : Dict [str , Any ],
68175 ) -> None :
69176 """Process a log record from the logging system.
70177
71178 Args:
179+ emitter: The emitter to emit the log record to.
72180 record: The Python logging.LogRecord to process.
73- log_model: The log model to emit the log record to.
74181 metadata: Additional metadata to attach to the log entry.
75182 """
76183
77184 @abstractmethod
78- def finalize (
185+ def _finalize (
79186 self ,
80- log_model : LogsResponse ,
187+ emitter : BaseLogStoreEmitter ,
81188 ) -> None :
82- """Finalize the stream of log records associated with a log model .
189+ """Finalize the stream of log records associated with an emitter .
83190
84191 This is used to announce the end of the stream of log records associated
85- with a log model and that no more log records will be emitted.
192+ with an emitter and that no more log records will be emitted.
86193
87194 The implementation should ensure that all log records associated with
88- the log model are flushed to the backend and any resources (clients,
195+ the emitter are flushed to the backend and any resources (clients,
89196 connections, file descriptors, etc.) are released.
90197
91198 Args:
92- log_model : The log model to finalize.
199+ emitter : The emitter to finalize.
93200 """
94201
95- def register_emitter (self ) -> None :
96- """Register an emitter for the log store."""
97- with self ._lock :
98- self ._emitter_counter += 1
99-
100- def deregister_emitter (self ) -> None :
101- """Deregister an emitter for the log store."""
102- with self ._lock :
103- self ._emitter_counter -= 1
104- if self ._emitter_counter == 0 :
105- self .flush ()
106-
107202 @abstractmethod
108- def flush (self ) -> None :
203+ def flush (self , blocking : bool = True ) -> None :
109204 """Flush the log store.
110205
111206 This method is called to ensure that all logs are flushed to the backend.
207+
208+ Args:
209+ blocking: Whether to block until the flush is complete.
112210 """
113211
114212 @abstractmethod
0 commit comments