wallaroo.logs
1import datetime 2import json 3from typing import Any, Dict, List 4 5import requests 6 7from .inference_decode import decode_inference_result, to_nd_array_list 8from .version import _user_agent 9 10 11def fetch_plateau_logs(server: str, topic: str, limit: int = 100): 12 headers = {"User-Agent": _user_agent} 13 r = requests.get(f"http://{server}/topic/{topic}/", headers=headers) 14 metadata = r.json() 15 16 partitions = metadata["partitions"] 17 records = [] 18 n = (limit // len(partitions)) + 1 19 for partition, indices in partitions.items(): 20 end = indices["end"] 21 query = {"start": end - n, "limit": n} 22 partition_records = requests.get( 23 f"http://{server}/topic/{topic}/{partition}/records", 24 query, 25 headers=headers, 26 ) 27 records.extend(partition_records.json()["records"]) 28 29 return LogEntries(LogEntry(json.loads(r)) for r in records[:limit]) 30 31 32class LogEntry(object): 33 """Wraps a single log entry. 34 35 This class is highly experimental, is unsupported/untested, and may 36 change/disappear in the near future. 37 """ 38 39 def __init__(self, entry: Dict[str, Any]) -> None: 40 self.elapsed = entry["elapsed"] 41 self.model_name = entry["model_name"] # TODO: refer to actual model 42 self.model_version = entry["model_version"] # TODO: refer to actual model 43 44 if len(entry["original_data"]) == 1: 45 # We will assume its key and grab its value 46 self.input = next(iter(entry["original_data"].values())) 47 else: 48 # multiple inputs 49 self.input = entry["original_data"] 50 51 self.output = to_nd_array_list(decode_inference_result(entry)) 52 self.validation_failures = entry.get("check_failures") or [] 53 self.timestamp = datetime.datetime.fromtimestamp(entry["time"] / 1000) 54 self.raw = entry 55 if "shadow_data" in entry: 56 self.shadow_data = entry["shadow_data"] 57 else: 58 self.shadow_data = {} 59 60 61class LogEntries(List[LogEntry]): 62 """Wraps a list of log entries. 63 64 This class is highly experimental, is unsupported/untested, and may 65 change/disappear in the near future. 66 """ 67 68 def _repr_html_(self) -> str: 69 def style(r): 70 return "color: red;" if len(r.validation_failures) > 0 else "" 71 72 rows = [ 73 f""" 74 <tr style="{style(r)}"> 75 <td>{r.timestamp.strftime("%Y-%d-%b %H:%M:%S")}</td> 76 <td>{r.output}</td> 77 <td>{r.input}</td> 78 <td>{len(r.validation_failures)}</td> 79 </tr> 80 """ 81 for r in self 82 ] 83 table = """ 84 <table> 85 <tr> 86 <th>Timestamp</th> 87 <th>Output</th> 88 <th>Input</th> 89 <th>Anomalies</th> 90 </tr> 91 {0} 92 </table> 93 """.format( 94 "\n".join(rows) 95 ) 96 return table 97 98 99class LogEntriesShadowDeploy(LogEntries): 100 def __init__(self, logs: LogEntries): 101 self.logs = filter(lambda log: len(log.shadow_data.keys()) > 0, logs) 102 103 def _repr_html_(self) -> str: 104 logs = self.logs 105 rows: List[str] = [] 106 for index, result in enumerate(logs): 107 rows += """ 108 <tr><td colspan='6'>Log Entry {}</td></tr> 109 <tr><td colspan='6'></td></tr> 110 <tr> 111 <td> 112 <strong><em>Input</em></strong> 113 </td> 114 <td colspan='6'>{}</td> 115 </tr> 116 """.format( 117 index, result.input 118 ) 119 rows += """ 120 <tr> 121 <td>Model Type</td> 122 <td> 123 <strong>Model Name</strong> 124 </td> 125 <td> 126 <strong>Output</strong> 127 </td> 128 <td> 129 <strong>Timestamp</strong> 130 </td> 131 <td> 132 <strong>Model Version</strong> 133 </td> 134 <td> 135 <strong>Elapsed</strong> 136 </td> 137 </tr> 138 <tr> 139 <td><strong><em>Primary</em></strong></td> 140 <td>{}</td> 141 <td>{}</td> 142 <td>{}</td> 143 <td>{}</td> 144 <td>{}</td> 145 </tr> 146 """.format( 147 result.model_name, 148 result.output, 149 result.timestamp.isoformat(), 150 result.model_version, 151 result.elapsed, 152 ) 153 for shadow_model, shadow_result in result.shadow_data.items(): 154 rows += """ 155 <tr> 156 <td><strong><em>Challenger</em></strong></td> 157 <td>{}</td> 158 <td>{}</td> 159 <td colspan=3></td> 160 </tr> 161 """.format( 162 shadow_model, shadow_result 163 ) 164 return """ 165 <h2>Shadow Deploy Logs</h2> 166 <p> 167 <em>Logs from a shadow pipeline, grouped by their input.</em> 168 </p> 169 <table> 170 <tbody> 171 {} 172 </tbody> 173 <table> 174 """.format( 175 "".join(rows) 176 )
def
fetch_plateau_logs(server: str, topic: str, limit: int = 100):
12def fetch_plateau_logs(server: str, topic: str, limit: int = 100): 13 headers = {"User-Agent": _user_agent} 14 r = requests.get(f"http://{server}/topic/{topic}/", headers=headers) 15 metadata = r.json() 16 17 partitions = metadata["partitions"] 18 records = [] 19 n = (limit // len(partitions)) + 1 20 for partition, indices in partitions.items(): 21 end = indices["end"] 22 query = {"start": end - n, "limit": n} 23 partition_records = requests.get( 24 f"http://{server}/topic/{topic}/{partition}/records", 25 query, 26 headers=headers, 27 ) 28 records.extend(partition_records.json()["records"]) 29 30 return LogEntries(LogEntry(json.loads(r)) for r in records[:limit])
class
LogEntry:
33class LogEntry(object): 34 """Wraps a single log entry. 35 36 This class is highly experimental, is unsupported/untested, and may 37 change/disappear in the near future. 38 """ 39 40 def __init__(self, entry: Dict[str, Any]) -> None: 41 self.elapsed = entry["elapsed"] 42 self.model_name = entry["model_name"] # TODO: refer to actual model 43 self.model_version = entry["model_version"] # TODO: refer to actual model 44 45 if len(entry["original_data"]) == 1: 46 # We will assume its key and grab its value 47 self.input = next(iter(entry["original_data"].values())) 48 else: 49 # multiple inputs 50 self.input = entry["original_data"] 51 52 self.output = to_nd_array_list(decode_inference_result(entry)) 53 self.validation_failures = entry.get("check_failures") or [] 54 self.timestamp = datetime.datetime.fromtimestamp(entry["time"] / 1000) 55 self.raw = entry 56 if "shadow_data" in entry: 57 self.shadow_data = entry["shadow_data"] 58 else: 59 self.shadow_data = {}
Wraps a single log entry.
This class is highly experimental, is unsupported/untested, and may change/disappear in the near future.
LogEntry(entry: Dict[str, Any])
40 def __init__(self, entry: Dict[str, Any]) -> None: 41 self.elapsed = entry["elapsed"] 42 self.model_name = entry["model_name"] # TODO: refer to actual model 43 self.model_version = entry["model_version"] # TODO: refer to actual model 44 45 if len(entry["original_data"]) == 1: 46 # We will assume its key and grab its value 47 self.input = next(iter(entry["original_data"].values())) 48 else: 49 # multiple inputs 50 self.input = entry["original_data"] 51 52 self.output = to_nd_array_list(decode_inference_result(entry)) 53 self.validation_failures = entry.get("check_failures") or [] 54 self.timestamp = datetime.datetime.fromtimestamp(entry["time"] / 1000) 55 self.raw = entry 56 if "shadow_data" in entry: 57 self.shadow_data = entry["shadow_data"] 58 else: 59 self.shadow_data = {}
class
LogEntries(typing.List[wallaroo.logs.LogEntry]):
62class LogEntries(List[LogEntry]): 63 """Wraps a list of log entries. 64 65 This class is highly experimental, is unsupported/untested, and may 66 change/disappear in the near future. 67 """ 68 69 def _repr_html_(self) -> str: 70 def style(r): 71 return "color: red;" if len(r.validation_failures) > 0 else "" 72 73 rows = [ 74 f""" 75 <tr style="{style(r)}"> 76 <td>{r.timestamp.strftime("%Y-%d-%b %H:%M:%S")}</td> 77 <td>{r.output}</td> 78 <td>{r.input}</td> 79 <td>{len(r.validation_failures)}</td> 80 </tr> 81 """ 82 for r in self 83 ] 84 table = """ 85 <table> 86 <tr> 87 <th>Timestamp</th> 88 <th>Output</th> 89 <th>Input</th> 90 <th>Anomalies</th> 91 </tr> 92 {0} 93 </table> 94 """.format( 95 "\n".join(rows) 96 ) 97 return table
Wraps a list of log entries.
This class is highly experimental, is unsupported/untested, and may change/disappear in the near future.
Inherited Members
- builtins.list
- list
- clear
- copy
- append
- insert
- extend
- pop
- remove
- index
- count
- reverse
- sort
class
LogEntriesShadowDeploy(typing.List[wallaroo.logs.LogEntry]):
100class LogEntriesShadowDeploy(LogEntries): 101 def __init__(self, logs: LogEntries): 102 self.logs = filter(lambda log: len(log.shadow_data.keys()) > 0, logs) 103 104 def _repr_html_(self) -> str: 105 logs = self.logs 106 rows: List[str] = [] 107 for index, result in enumerate(logs): 108 rows += """ 109 <tr><td colspan='6'>Log Entry {}</td></tr> 110 <tr><td colspan='6'></td></tr> 111 <tr> 112 <td> 113 <strong><em>Input</em></strong> 114 </td> 115 <td colspan='6'>{}</td> 116 </tr> 117 """.format( 118 index, result.input 119 ) 120 rows += """ 121 <tr> 122 <td>Model Type</td> 123 <td> 124 <strong>Model Name</strong> 125 </td> 126 <td> 127 <strong>Output</strong> 128 </td> 129 <td> 130 <strong>Timestamp</strong> 131 </td> 132 <td> 133 <strong>Model Version</strong> 134 </td> 135 <td> 136 <strong>Elapsed</strong> 137 </td> 138 </tr> 139 <tr> 140 <td><strong><em>Primary</em></strong></td> 141 <td>{}</td> 142 <td>{}</td> 143 <td>{}</td> 144 <td>{}</td> 145 <td>{}</td> 146 </tr> 147 """.format( 148 result.model_name, 149 result.output, 150 result.timestamp.isoformat(), 151 result.model_version, 152 result.elapsed, 153 ) 154 for shadow_model, shadow_result in result.shadow_data.items(): 155 rows += """ 156 <tr> 157 <td><strong><em>Challenger</em></strong></td> 158 <td>{}</td> 159 <td>{}</td> 160 <td colspan=3></td> 161 </tr> 162 """.format( 163 shadow_model, shadow_result 164 ) 165 return """ 166 <h2>Shadow Deploy Logs</h2> 167 <p> 168 <em>Logs from a shadow pipeline, grouped by their input.</em> 169 </p> 170 <table> 171 <tbody> 172 {} 173 </tbody> 174 <table> 175 """.format( 176 "".join(rows) 177 )
Wraps a list of log entries.
This class is highly experimental, is unsupported/untested, and may change/disappear in the near future.
LogEntriesShadowDeploy(logs: wallaroo.logs.LogEntries)
Inherited Members
- builtins.list
- clear
- copy
- append
- insert
- extend
- pop
- remove
- index
- count
- reverse
- sort