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)
101    def __init__(self, logs: LogEntries):
102        self.logs = filter(lambda log: len(log.shadow_data.keys()) > 0, logs)
Inherited Members
builtins.list
clear
copy
append
insert
extend
pop
remove
index
count
reverse
sort