robo-goggles 0.1.7__tar.gz
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 robo-goggles might be problematic. Click here for more details.
- robo_goggles-0.1.7/LICENSE +21 -0
- robo_goggles-0.1.7/PKG-INFO +601 -0
- robo_goggles-0.1.7/README.md +552 -0
- robo_goggles-0.1.7/goggles/__init__.py +859 -0
- robo_goggles-0.1.7/goggles/_core/decorators.py +103 -0
- robo_goggles-0.1.7/goggles/_core/integrations/__init__.py +29 -0
- robo_goggles-0.1.7/goggles/_core/integrations/console.py +125 -0
- robo_goggles-0.1.7/goggles/_core/integrations/storage.py +385 -0
- robo_goggles-0.1.7/goggles/_core/integrations/wandb.py +314 -0
- robo_goggles-0.1.7/goggles/_core/logger.py +606 -0
- robo_goggles-0.1.7/goggles/_core/routing.py +126 -0
- robo_goggles-0.1.7/goggles/config.py +68 -0
- robo_goggles-0.1.7/goggles/history/__init__.py +39 -0
- robo_goggles-0.1.7/goggles/history/buffer.py +186 -0
- robo_goggles-0.1.7/goggles/history/spec.py +144 -0
- robo_goggles-0.1.7/goggles/history/types.py +9 -0
- robo_goggles-0.1.7/goggles/history/utils.py +191 -0
- robo_goggles-0.1.7/goggles/media.py +284 -0
- robo_goggles-0.1.7/goggles/shutdown.py +69 -0
- robo_goggles-0.1.7/goggles/types.py +81 -0
- robo_goggles-0.1.7/goggles/validation.py +35 -0
- robo_goggles-0.1.7/pyproject.toml +180 -0
- robo_goggles-0.1.7/robo_goggles.egg-info/PKG-INFO +601 -0
- robo_goggles-0.1.7/robo_goggles.egg-info/SOURCES.txt +27 -0
- robo_goggles-0.1.7/robo_goggles.egg-info/dependency_links.txt +1 -0
- robo_goggles-0.1.7/robo_goggles.egg-info/requires.txt +25 -0
- robo_goggles-0.1.7/robo_goggles.egg-info/top_level.txt +1 -0
- robo_goggles-0.1.7/setup.cfg +4 -0
- robo_goggles-0.1.7/tests/test_api.py +211 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Antonio Terpin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: robo-goggles
|
|
3
|
+
Version: 0.1.7
|
|
4
|
+
Summary: Observability framework for robotics research
|
|
5
|
+
Author-email: Antonio Terpin <aterpin@ethz.ch>, Francesco Banelli <fbanelli@ethz.ch>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/antonioterpin/goggles
|
|
8
|
+
Project-URL: Repository, https://github.com/antonioterpin/goggles
|
|
9
|
+
Project-URL: Documentation, https://github.com/antonioterpin/goggles#readme
|
|
10
|
+
Project-URL: Issues, https://github.com/antonioterpin/goggles/issues
|
|
11
|
+
Keywords: robotics,logging,observability,jax,wandb,experiment-tracking
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: System :: Logging
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: ruamel.yaml
|
|
27
|
+
Requires-Dist: rich
|
|
28
|
+
Requires-Dist: portal>=3.7.3
|
|
29
|
+
Requires-Dist: typing-extensions>=4.15.0
|
|
30
|
+
Requires-Dist: netifaces>=0.11.0
|
|
31
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
32
|
+
Requires-Dist: numpy>=1.23
|
|
33
|
+
Requires-Dist: imageio>=2.37.0
|
|
34
|
+
Requires-Dist: matplotlib>=3.10.7
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: hypothesis>=6.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: snowballstemmer==2.2.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pre_commit==4.0.1; extra == "dev"
|
|
41
|
+
Requires-Dist: tomli; extra == "dev"
|
|
42
|
+
Requires-Dist: pyupgrade>=3.19.0; extra == "dev"
|
|
43
|
+
Provides-Extra: jax
|
|
44
|
+
Requires-Dist: jax>=0.4.0; extra == "jax"
|
|
45
|
+
Requires-Dist: jaxlib>=0.4.0; extra == "jax"
|
|
46
|
+
Provides-Extra: wandb
|
|
47
|
+
Requires-Dist: wandb[media]; extra == "wandb"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# π Goggles - Observability for Robotics Research
|
|
51
|
+
|
|
52
|
+
[](https://www.python.org/downloads/)
|
|
53
|
+
[](https://github.com/antonioterpin/goggles/stargazers)
|
|
54
|
+
[](https://codecov.io/gh/antonioterpin/goggles)
|
|
55
|
+
[](https://github.com/antonioterpin/goggles/actions/workflows/test.yaml)
|
|
56
|
+
[](https://github.com/antonioterpin/goggles/actions/workflows/code-style.yaml)
|
|
57
|
+
[](https://pypi.org/project/robo-goggles)
|
|
58
|
+
[](https://opensource.org/licenses/MIT)
|
|
59
|
+
[](https://github.com/astral-sh/uv)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
A lightweight, flexible Python observability framework designed for robotics research. Goggles provides structured logging, experiment tracking, performance profiling, and device-resident temporal memory management for JAX-based pipelines.
|
|
63
|
+
|
|
64
|
+
## β¨ Features
|
|
65
|
+
|
|
66
|
+
- π€ **Multi-process (and multi-machines) logging** - Synchronize logs across spawned processes reliably and efficiently (shared memory when available).
|
|
67
|
+
- π― **Multi-output support** - Log to console, files, and remote services simultaneously.
|
|
68
|
+
- π **Experiment tracking** - Native integration with Weights & Biases for metrics, images, and videos.
|
|
69
|
+
- π **Performance profiling** - `@goggles.timeit` decorator for automatic runtime measurement.
|
|
70
|
+
- π **Error tracing** - `@goggles.trace_on_error` auto-logs full stack traces on exceptions.
|
|
71
|
+
- π§ **Device-resident histories** - JAX-based GPU memory management for efficient, long-running experiments metrics.
|
|
72
|
+
- π¦ **Graceful shutdown** - Automatic cleanup of resources and handlers.
|
|
73
|
+
- βοΈ **Structured configuration** - YAML-based config loading with validation.
|
|
74
|
+
- π **Extensible handlers** - Plugin architecture for custom logging backends.
|
|
75
|
+
|
|
76
|
+
## ποΈ Projects Built with Goggles
|
|
77
|
+
|
|
78
|
+
This framework has been battle-tested across multiple research projects:
|
|
79
|
+
|
|
80
|
+
[](https://github.com/antonioterpin/fluidscontrol)
|
|
81
|
+
[](https://github.com/antonioterpin/flowgym)
|
|
82
|
+
[](https://github.com/antonioterpin/synthpix)
|
|
83
|
+
[](https://github.com/antonioterpin/pinet)
|
|
84
|
+
[](https://github.com/antonioterpin/glitch)
|
|
85
|
+
|
|
86
|
+
## π Quick Start
|
|
87
|
+
|
|
88
|
+
### Installation
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Basic installation
|
|
92
|
+
uv add robo-goggles # or pip install robo-goggles
|
|
93
|
+
|
|
94
|
+
# With Weights & Biases support
|
|
95
|
+
uv add "robo-goggles[wandb]"
|
|
96
|
+
|
|
97
|
+
# With JAX device-resident histories
|
|
98
|
+
uv add "robo-goggles[jax]"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For the development installation, see our [How to contribute](./CONTRIBUTING.md) page.
|
|
102
|
+
|
|
103
|
+
### Basic usage
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import goggles as gg
|
|
107
|
+
import logging
|
|
108
|
+
|
|
109
|
+
# Set up console logging
|
|
110
|
+
logger = gg.get_logger("my_experiment")
|
|
111
|
+
gg.attach(
|
|
112
|
+
gg.ConsoleHandler(name="console", level=logging.INFO),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Basic logging
|
|
116
|
+
logger.info("Experiment started")
|
|
117
|
+
logger.warning("This is a warning")
|
|
118
|
+
logger.error("An error occurred")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
See also [Example 1](./examples/01_basic_run.py), which you can run after cloning the repo with
|
|
122
|
+
```bash
|
|
123
|
+
uv run examples/01_basic_run.py
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Experiment tracking with W&B
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
import goggles as gg
|
|
130
|
+
import numpy as np
|
|
131
|
+
|
|
132
|
+
# Enable metrics logging
|
|
133
|
+
logger = gg.get_logger("experiment", with_metrics=True)
|
|
134
|
+
gg.attach(
|
|
135
|
+
gg.WandBHandler(project="my_project", name="run_1"),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Log metrics, images, and videos
|
|
139
|
+
for step in range(100):
|
|
140
|
+
logger.scalar("loss", np.random.random(), step=step)
|
|
141
|
+
logger.scalar("accuracy", 0.8 + 0.2 * np.random.random(), step=step)
|
|
142
|
+
|
|
143
|
+
# Log images and videos
|
|
144
|
+
image = np.random.randint(0, 255, (64, 64, 3), dtype=np.uint8)
|
|
145
|
+
logger.image(image, name="sample_image")
|
|
146
|
+
|
|
147
|
+
video = np.random.randint(0, 255, (30, 3, 64, 64), dtype=np.uint8)
|
|
148
|
+
logger.video(video, name="sample_video", fps=10)
|
|
149
|
+
|
|
150
|
+
# Ensure proper cleanup
|
|
151
|
+
gg.finish()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Performance profiling and error tracking
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import goggles as gg
|
|
158
|
+
import logging
|
|
159
|
+
|
|
160
|
+
class Trainer:
|
|
161
|
+
@gg.timeit(severity=logging.INFO)
|
|
162
|
+
def train_step(self, batch):
|
|
163
|
+
# Your training logic here
|
|
164
|
+
return {"loss": 0.1}
|
|
165
|
+
|
|
166
|
+
@gg.trace_on_error()
|
|
167
|
+
def risky_operation(self, data):
|
|
168
|
+
# This will log full traceback on any exception
|
|
169
|
+
return data / 0 # Will trigger trace logging
|
|
170
|
+
|
|
171
|
+
trainer = Trainer()
|
|
172
|
+
trainer.train_step({"x": [1, 2, 3]}) # Logs execution time
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
trainer.risky_operation(10)
|
|
176
|
+
except ZeroDivisionError:
|
|
177
|
+
pass # Full traceback was automatically logged
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Configuration Management
|
|
181
|
+
|
|
182
|
+
Load and validate YAML configurations:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
import goggles
|
|
186
|
+
|
|
187
|
+
# Load configuration with automatic validation
|
|
188
|
+
config = goggles.load_configuration("config.yaml")
|
|
189
|
+
print(config) # Pretty print
|
|
190
|
+
print(config["learning_rate"]) # Access as dict
|
|
191
|
+
|
|
192
|
+
# Pretty-print configuration
|
|
193
|
+
goggles.save_configuration(config, "output.yaml")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Supported Platforms π»
|
|
197
|
+
|
|
198
|
+
| Platform | Basic | W&B | JAX/GPU | Development |
|
|
199
|
+
|----------|-------|-----|---------|-------------|
|
|
200
|
+
| Linux | β
| β
| β
| β
|
|
|
201
|
+
| macOS | β
| β
| β
| β
|
|
|
202
|
+
| Windows | β
| β
| β | β
|
|
|
203
|
+
|
|
204
|
+
*GPU support requires CUDA-compatible hardware and drivers*
|
|
205
|
+
|
|
206
|
+
## π₯ Examples
|
|
207
|
+
|
|
208
|
+
Explore the `examples/` directory for comprehensive usage patterns:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Basic logging setup
|
|
212
|
+
uv run examples/01_basic_run.py
|
|
213
|
+
|
|
214
|
+
# Advanced: Multi-scope logging
|
|
215
|
+
uv run examples/02_multi_scope.py
|
|
216
|
+
|
|
217
|
+
# File-based logging (local storage)
|
|
218
|
+
uv run examples/03_local_storage.py
|
|
219
|
+
|
|
220
|
+
# Weights & Biases integration
|
|
221
|
+
uv run examples/04_wandb.py
|
|
222
|
+
|
|
223
|
+
# Advanced: Weights & Biases multi-run setup
|
|
224
|
+
uv run examples/05_wandb_multiple_runs.py
|
|
225
|
+
|
|
226
|
+
# Advanced: Custom handler
|
|
227
|
+
uv run exacmples/06_custom_handler.py
|
|
228
|
+
|
|
229
|
+
# Graceful shutdown utils
|
|
230
|
+
uv run examples/100_interrupt.py
|
|
231
|
+
|
|
232
|
+
# Pretty and convenient utils for configuration laoding
|
|
233
|
+
uv run examples/101_config.py
|
|
234
|
+
|
|
235
|
+
# Advanced: Performance decorators
|
|
236
|
+
uv run examples/102_decorators.py
|
|
237
|
+
|
|
238
|
+
# Advanced: JAX device-resident histories
|
|
239
|
+
uv run examples/103_history.py
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## π§ For Goggles power user
|
|
243
|
+
|
|
244
|
+
This section includes some cool functionalities of `goggles`. Enjoy!
|
|
245
|
+
|
|
246
|
+
### Multi-scope logging
|
|
247
|
+
Goggles allow easily to set up different handlers for different scopes. That is, one can have an handler attached to multiple scopes, and a scope having multiple handlers. Each logger is associated to a single scope (by default: `global`), and logging with that logger will invoke all the loggers associated with the scope.
|
|
248
|
+
|
|
249
|
+
#### Why?
|
|
250
|
+
Within the same run, we may have logs that belong to different scopes. An example is training in Reinforcement Learning, where in a single training run there are multiple episodes. A complete example for this is provided in the [multiple runs in WandB](#multiple-runs-in-wandb) section.
|
|
251
|
+
|
|
252
|
+
#### Usage
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
# In this example, we set up a handlers associated
|
|
256
|
+
# to different scopes.
|
|
257
|
+
handler1 = gg.ConsoleHandler(name="examples.basic.console.1", level=logging.INFO)
|
|
258
|
+
gg.attach(handler1, scopes=["global", "scope1"])
|
|
259
|
+
|
|
260
|
+
handler2 = gg.ConsoleHandler(name="examples.basic.console.2", level=logging.INFO)
|
|
261
|
+
gg.attach(handler2, scopes=["global", "scope2"])
|
|
262
|
+
|
|
263
|
+
# We need to get separate loggers for each scope
|
|
264
|
+
logger_scope1 = gg.get_logger("examples.basic.scope1", scope="scope1")
|
|
265
|
+
logger_scope2 = gg.get_logger("examples.basic.scope2")
|
|
266
|
+
logger_scope2.bind(scope="scope2") # You can also bind the scope after creation
|
|
267
|
+
logger_global = gg.get_logger("examples.basic.global", scope="global")
|
|
268
|
+
|
|
269
|
+
# Now we can log messages to different scopes, so that only the interested
|
|
270
|
+
# handlers will process them.
|
|
271
|
+
logger_scope1.info(f"This will be logged only by {handler1.name}")
|
|
272
|
+
logger_scope2.info(f"This will be logged only by {handler2.name}")
|
|
273
|
+
logger_global.info("This will be logged by both handlers.")
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
See also [examples/02_multi_scope.py](./examples/02_multi_scope.py) for a running example.
|
|
277
|
+
|
|
278
|
+
### Multiple runs in WandB
|
|
279
|
+
An example of the benefit of scopes is given by the WandBHandler, which instantiate a different WandB run for each scope and groups them together:
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
import goggles as gg
|
|
283
|
+
from goggles import WandBHandler
|
|
284
|
+
|
|
285
|
+
# In this example, we set up multiple runs in Weights & Biases (W&B).
|
|
286
|
+
# All runs created by the handler will be grouped under
|
|
287
|
+
# the same project and group.
|
|
288
|
+
logger: gg.GogglesLogger = gg.get_logger("examples.basic", with_metrics=True)
|
|
289
|
+
handler = WandBHandler(
|
|
290
|
+
project="goggles_example", reinit="create_new", group="multiple_runs"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# In particular, we set up multiple runs in an RL training loop, with each
|
|
294
|
+
# episode being a separate W&B run and a global run tracking all episodes.
|
|
295
|
+
num_episodes = 3
|
|
296
|
+
episode_length = 10
|
|
297
|
+
scopes = [f"episode_{episode}" for episode in range(num_episodes + 1)]
|
|
298
|
+
scopes.append("global")
|
|
299
|
+
gg.attach(handler, scopes=scopes)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def my_episode(index: int):
|
|
303
|
+
episode_logger = gg.get_logger(scope=f"episode_{index}", with_metrics=True)
|
|
304
|
+
for step in range(episode_length):
|
|
305
|
+
# Supports scopes transparently
|
|
306
|
+
# and has its own step counter
|
|
307
|
+
episode_logger.scalar("env/reward", index * episode_length + step, step=step)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
for i in range(num_episodes):
|
|
311
|
+
my_episode(i)
|
|
312
|
+
logger.scalar("total_reward", i, step=i)
|
|
313
|
+
|
|
314
|
+
# When using asynchronous logging (like wandb), make sure to finish
|
|
315
|
+
gg.finish()
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Fully asynchronous logging
|
|
319
|
+
As in the WandB example, all the handlers work in the background. By default, the logging calls are blocking, but can be made not blocking by setting the environment variable `GOGGLES_ASYNC` to `1` or `true`. When you use the async mode, remember to call `gg.finish()` at the end from your host machine!
|
|
320
|
+
>[!WARNING]
|
|
321
|
+
> This functionality still needs thorough tesing, as well as a better documentation. Help is appreciated! π€
|
|
322
|
+
|
|
323
|
+
### Multi-machine logging
|
|
324
|
+
Goggles provides options to synchronize logging across machines, since there is always only a single server active. The relevant environment variables here are `GOGGLES_HOST` and `GOGGLES_PORT`.
|
|
325
|
+
>[!WARNING]
|
|
326
|
+
> This functionality still needs thorough tesing, as well as a better documentation. Help is appreciated! π€
|
|
327
|
+
|
|
328
|
+
### Adding a custom handler
|
|
329
|
+
> [!NOTE]
|
|
330
|
+
> Ideally, you should open a PR: We would love to integrate your work!
|
|
331
|
+
|
|
332
|
+
Adding a custom handler is straightforward:
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
import goggles as gg
|
|
336
|
+
import logging
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class CustomConsoleHandler(gg.ConsoleHandler):
|
|
340
|
+
"""A custom console handler that adds a prefix to each log message."""
|
|
341
|
+
|
|
342
|
+
def handle(self, event: gg.Event) -> None:
|
|
343
|
+
dict = event.to_dict()
|
|
344
|
+
|
|
345
|
+
dict["payload"] = f"[CUSTOM PREFIX] {dict['payload']}"
|
|
346
|
+
|
|
347
|
+
event = gg.Event.from_dict(dict)
|
|
348
|
+
super().handle(event)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# Register the custom handler so it can be serialized/deserialized
|
|
352
|
+
gg.register_handler(CustomConsoleHandler)
|
|
353
|
+
|
|
354
|
+
# In this basic example, we set up a logger that outputs to the console.
|
|
355
|
+
logger = gg.get_logger("examples.custom_handler")
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
gg.attach(
|
|
359
|
+
CustomConsoleHandler(name="examples.custom.console", level=logging.INFO),
|
|
360
|
+
scopes=["global"],
|
|
361
|
+
)
|
|
362
|
+
# Because the logging level is set to INFO, the debug message will not be shown.
|
|
363
|
+
logger.info("Hello, world!")
|
|
364
|
+
logger.debug("you won't see this at INFO")
|
|
365
|
+
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
See also [examples/05_custom_handler.py](./examples/06_custom_handler.py) for a complete example.
|
|
369
|
+
|
|
370
|
+
### Device-resident histories
|
|
371
|
+
For long-running GPU experiments that need efficient temporal memory management:
|
|
372
|
+
|
|
373
|
+
#### Why?
|
|
374
|
+
|
|
375
|
+
During development of fluid control experiments and reinforcement learning pipelines, we needed to:
|
|
376
|
+
- Track detailed metrics during GPU-accelerated training
|
|
377
|
+
- Avoid expensive device-to-host transfers
|
|
378
|
+
- Maintain temporal state across episodes
|
|
379
|
+
- Support JIT compilation for maximum performance
|
|
380
|
+
|
|
381
|
+
#### Features
|
|
382
|
+
|
|
383
|
+
- **Pure functional** and **JIT-safe** buffer updates
|
|
384
|
+
- **Per-field history lengths** with episodic reset support
|
|
385
|
+
- **Batch-first convention**: `(B, T, *shape)` for all tensors
|
|
386
|
+
- **Zero host-device synchronization** during updates
|
|
387
|
+
- **Integrated with FlowGym's** `EstimatorState` for temporal RL memory
|
|
388
|
+
|
|
389
|
+
#### Usage
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
from goggles.history import HistorySpec, create_history, update_history
|
|
393
|
+
import jax.numpy as jnp
|
|
394
|
+
|
|
395
|
+
# Define what to track over time
|
|
396
|
+
spec = HistorySpec.from_config({
|
|
397
|
+
"states": {"length": 100, "shape": (64, 64, 2), "dtype": jnp.float32},
|
|
398
|
+
"actions": {"length": 50, "shape": (8,), "dtype": jnp.float32},
|
|
399
|
+
"rewards": {"length": 100, "shape": (), "dtype": jnp.float32},
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
# Create GPU-resident history buffers
|
|
403
|
+
history = create_history(spec, batch_size=32)
|
|
404
|
+
print(history["states"].shape) # (32, 100, 64, 64, 2)
|
|
405
|
+
|
|
406
|
+
# Update buffers during training (JIT-compiled)
|
|
407
|
+
new_state = jnp.ones((32, 64, 64, 2))
|
|
408
|
+
history = update_history(history, {"states": new_state})
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
See also [examples/103_history.py](./examples/103_history.py) for a running example.
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
## π€ Contributing
|
|
415
|
+
|
|
416
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for detailed information on:
|
|
417
|
+
|
|
418
|
+
β’ Development workflow and environment setup
|
|
419
|
+
β’ Code style requirements and automated checks
|
|
420
|
+
β’ Testing standards and coverage expectations
|
|
421
|
+
β’ PR preparation and commit message conventions
|
|
422
|
+
|
|
423
|
+
## π License
|
|
424
|
+
|
|
425
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
*Ready to enhance your robotics research with structured observability? Get started with Goggles today! π*gles
|
|
430
|
+
|
|
431
|
+
A lightweight, flexible Python logging and monitoring library designed to simplify and enhance experiment tracking, performance profiling, and error tracing. Integrates with terminal, file-based logs, and W\&B (Weights & Biases). It is thought primarily for research projects in robotics.
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
pip install "goggles @ git+ssh://git@github.com/antonioterpin/goggles.git"
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Features
|
|
438
|
+
|
|
439
|
+
- π€ **Multi-process, single-thread compatible**
|
|
440
|
+
Synchronize logs from all spawned processes via shared memory.
|
|
441
|
+
|
|
442
|
+
- π― **Multi-output logging**
|
|
443
|
+
Log to terminal and/or file.
|
|
444
|
+
|
|
445
|
+
- π **Performance profiling**
|
|
446
|
+
`@goggles.timeit` decorator measures and logs runtime.
|
|
447
|
+
|
|
448
|
+
- π **Error tracing**
|
|
449
|
+
`@goggles.trace_on_error` auto-logs full stack on exceptions.
|
|
450
|
+
|
|
451
|
+
- π **Metrics tracking**
|
|
452
|
+
`goggles.scalar`, `goggles.vector`, `goggles.image`, `goggles.video` β Weights & Biases.
|
|
453
|
+
|
|
454
|
+
- π¦ **Graceful shutdown**
|
|
455
|
+
Call `goggles.cleanup()` (or hook into your own `signal` handler).
|
|
456
|
+
|
|
457
|
+
- βοΈ **Asynchronous scheduling**
|
|
458
|
+
Offload heavy logging tasks via `goggles.schedule_log(...)`.
|
|
459
|
+
|
|
460
|
+
- π **Pretty configuration loading**
|
|
461
|
+
`goggles.load_configuration(...)` loads YAML with validation.
|
|
462
|
+
|
|
463
|
+
## Quickstart
|
|
464
|
+
|
|
465
|
+
1. TODO: update
|
|
466
|
+
|
|
467
|
+
2. **Ready to log**:
|
|
468
|
+
|
|
469
|
+
```python
|
|
470
|
+
import goggles
|
|
471
|
+
|
|
472
|
+
goggles.debug("Debugging detailsβ¦")
|
|
473
|
+
goggles.info("Experiment started")
|
|
474
|
+
goggles.warning("This is a warning")
|
|
475
|
+
goggles.error("An error occurred")
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
We cleanup all the resources automatically at exit.
|
|
479
|
+
|
|
480
|
+
## Configuration
|
|
481
|
+
|
|
482
|
+
Pretty logging of configuration files.
|
|
483
|
+
|
|
484
|
+
```python
|
|
485
|
+
import goggles
|
|
486
|
+
|
|
487
|
+
# Load from examples/example_config.yaml
|
|
488
|
+
config = goggles.load_configuration("examples/example_config.yaml")
|
|
489
|
+
print(config)
|
|
490
|
+
|
|
491
|
+
# Access as dict
|
|
492
|
+
print(f"time_per_experiment = {config['time_per_experiment']}")
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## Decorators: `@goggles.timeit` and `@goggles.trace_on_error`
|
|
496
|
+
|
|
497
|
+
Measure execution time of methods or functions:
|
|
498
|
+
|
|
499
|
+
```python
|
|
500
|
+
import goggles
|
|
501
|
+
|
|
502
|
+
class Worker:
|
|
503
|
+
@goggles.timeit(severity=logging.DEBUG)
|
|
504
|
+
def compute_heavy(self, n):
|
|
505
|
+
return sum(range(n))
|
|
506
|
+
|
|
507
|
+
@goggles.trace_on_error()
|
|
508
|
+
def risky_division(self, x, y):
|
|
509
|
+
return x / y
|
|
510
|
+
|
|
511
|
+
g = Worker()
|
|
512
|
+
g.compute_heavy(1_000_000)
|
|
513
|
+
|
|
514
|
+
try:
|
|
515
|
+
g.risky_division(1, 0)
|
|
516
|
+
except ZeroDivisionError:
|
|
517
|
+
pass # Full traceback was logged
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## W\&B Integration
|
|
521
|
+
|
|
522
|
+
Log scalars, vectors, images, and videos directly to Weights & Biases:
|
|
523
|
+
|
|
524
|
+
```python
|
|
525
|
+
import goggles
|
|
526
|
+
from PIL import Image
|
|
527
|
+
import numpy as np
|
|
528
|
+
|
|
529
|
+
# Start or switch a W&B run
|
|
530
|
+
goggles.new_wandb_run(name="exp-run", config={"lr":1e-3, "batch":32})
|
|
531
|
+
|
|
532
|
+
# Scalars & histograms
|
|
533
|
+
goggles.scalar("accuracy", 0.92)
|
|
534
|
+
goggles.vector("loss_curve", [0.5,0.4,0.3])
|
|
535
|
+
|
|
536
|
+
# Images
|
|
537
|
+
img = Image.fromarray((np.random.rand(64,64,3)*255).astype(np.uint8))
|
|
538
|
+
goggles.image("random_image", img)
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## Graceful Shutdown
|
|
542
|
+
|
|
543
|
+
Cleanly handle interrupts (e.g., Ctrl-C) and perform cleanup:
|
|
544
|
+
|
|
545
|
+
```python
|
|
546
|
+
import goggles
|
|
547
|
+
from PIL import Image
|
|
548
|
+
import numpy as np
|
|
549
|
+
|
|
550
|
+
# Start or switch a W&B run
|
|
551
|
+
goggles.new_wandb_run(name="exp-run", config={"lr":1e-3, "batch":32})
|
|
552
|
+
|
|
553
|
+
# Scalars & histograms
|
|
554
|
+
goggles.scalar("accuracy", 0.92)
|
|
555
|
+
goggles.vector("loss_curve", [0.5,0.4,0.3])
|
|
556
|
+
|
|
557
|
+
# Images
|
|
558
|
+
img = Image.fromarray((np.random.rand(64,64,3)*255).astype(np.uint8))
|
|
559
|
+
goggles.image("random_image", img)
|
|
560
|
+
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## Asynchronous Logging & Video
|
|
564
|
+
|
|
565
|
+
Offload heavy logging tasks to worker threads and log video sequences:
|
|
566
|
+
|
|
567
|
+
```python
|
|
568
|
+
import goggles, numpy as np, time
|
|
569
|
+
from PIL import Image
|
|
570
|
+
|
|
571
|
+
goggles.new_wandb_run("video_demo", {})
|
|
572
|
+
goggles.init_scheduler(num_workers=4)
|
|
573
|
+
|
|
574
|
+
def save_and_log_frame(frame, idx):v
|
|
575
|
+
path = f"/tmp/frame_{idx}.png"
|
|
576
|
+
frame.save(path)
|
|
577
|
+
goggles.image(f"frame_{idx}", frame)
|
|
578
|
+
|
|
579
|
+
for i in range(100):
|
|
580
|
+
arr = (np.random.rand(64,64,3)*255).astype(np.uint8)
|
|
581
|
+
img = Image.fromarray(arr)
|
|
582
|
+
goggles.schedule_log(save_and_log_frame, img, i)
|
|
583
|
+
goggles.scalar("queue_size", goggles._task_queue.qsize())
|
|
584
|
+
|
|
585
|
+
goggles.stop_workers()
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Full list of running examples
|
|
589
|
+
|
|
590
|
+
We prepared an `examples/` folder with scripts covering:
|
|
591
|
+
|
|
592
|
+
TODO
|
|
593
|
+
|
|
594
|
+
## Contributing
|
|
595
|
+
|
|
596
|
+
PRs, issues, and feature requests are welcome! Open an issue or submit a PR on GitHub.
|
|
597
|
+
See our [contributing guide](./CONTRIBUTING.md) for more information.
|
|
598
|
+
|
|
599
|
+
## License
|
|
600
|
+
|
|
601
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|