Skip to content

Commit f47a669

Browse files
authored
Merge pull request #1154 from mlco2/docs/boamps
BoAmps output doc and example
2 parents 4562a4a + 4b71fc7 commit f47a669

8 files changed

Lines changed: 573 additions & 164 deletions

File tree

codecarbon/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
OfflineEmissionsTracker,
99
track_emissions,
1010
)
11+
from .output import OutputMethod
1112

12-
__all__ = ["EmissionsTracker", "OfflineEmissionsTracker", "track_emissions"]
13+
__all__ = [
14+
"EmissionsTracker",
15+
"OfflineEmissionsTracker",
16+
"OutputMethod",
17+
"track_emissions",
18+
]
1319
__app_name__ = "codecarbon"

codecarbon/emissions_tracker.py

Lines changed: 266 additions & 162 deletions
Large diffs are not rendered by default.

codecarbon/output.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Provides functionality for persistence of data
33
"""
44

5-
from codecarbon.output_methods.base_output import BaseOutput # noqa: F401
5+
from codecarbon.output_methods.base_output import BaseOutput, OutputMethod # noqa: F401
66

77
# Output to BoAmps format
88
from codecarbon.output_methods.boamps import BoAmpsOutput # noqa: F401

codecarbon/output_methods/base_output.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,35 @@
1+
from enum import Enum
12
from typing import List
23

34
from codecarbon.output_methods.emissions_data import EmissionsData, TaskEmissionsData
45

56

7+
class OutputMethod(str, Enum):
8+
"""
9+
Enum listing the available output methods.
10+
11+
Usage::
12+
13+
tracker = EmissionsTracker(
14+
output_methods=[OutputMethod.CSV, OutputMethod.API]
15+
)
16+
17+
Available values: ``CSV``, ``API``, ``LOGGER``, ``PROMETHEUS``,
18+
``LOGFIRE``, ``BOAMPS``.
19+
20+
.. note::
21+
HTTP output is not configured here; it is enabled by setting the
22+
``emissions_endpoint`` parameter.
23+
"""
24+
25+
CSV = "csv"
26+
API = "api"
27+
LOGGER = "logger"
28+
PROMETHEUS = "prometheus"
29+
LOGFIRE = "logfire"
30+
BOAMPS = "boamps"
31+
32+
633
class BaseOutput:
734
"""
835
An abstract class defining possible contracts for an output strategy, a strategy implementation can save emissions

docs/how-to/examples.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ The directory [examples/](https://github.com/mlco2/codecarbon/tree/master/exampl
6161

6262
| Example | Type | Description |
6363
|---------|------|-------------|
64+
| [boamps_output.py](https://github.com/mlco2/codecarbon/blob/master/examples/boamps_output.py) | Python Script | Write the output in [BoAmps](https://github.com/Boavizta/BoAmps) format. |
6465
| [logging_to_file.py](https://github.com/mlco2/codecarbon/blob/master/examples/logging_to_file.py) | Python Script | Save emissions data to a local CSV file |
6566
| [logging_to_file_exclusive_run.py](https://github.com/mlco2/codecarbon/blob/master/examples/logging_to_file_exclusive_run.py) | Python Script | Long-running process with exclusive file logging |
6667
| [logging_to_google_cloud.py](https://github.com/mlco2/codecarbon/blob/master/examples/logging_to_google_cloud.py) | Python Script | Send emissions data to Google Cloud Logging |

docs/reference/output.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
# Output
22

3+
## Choosing output methods
4+
5+
Use the `output_methods` parameter to select where emissions data is sent. It takes
6+
a list of `OutputMethod` enum values:
7+
8+
```python-skip
9+
from codecarbon import EmissionsTracker, OutputMethod
10+
11+
tracker = EmissionsTracker(
12+
output_methods=[OutputMethod.CSV, OutputMethod.API],
13+
)
14+
```
15+
16+
Available values: `CSV`, `API`, `LOGGER`, `PROMETHEUS`, `LOGFIRE`, `BOAMPS`.
17+
It can also be set in the config file as a comma-separated string, e.g.
18+
`output_methods=csv,api`. HTTP output is enabled separately via the
19+
`emissions_endpoint` parameter.
20+
21+
!!! warning "Deprecation"
22+
The individual `save_to_file`, `save_to_api`, `save_to_logger`,
23+
`save_to_prometheus` and `save_to_logfire` parameters are deprecated and will be
24+
removed in a future version. Use `output_methods` instead. When `output_methods`
25+
is provided, the `save_to_*` flags are ignored.
26+
327
## CSV
428

529
The package has an in-built logger that logs data into a CSV file named `emissions.csv` in the `output_dir`, provided as an input parameter (defaults to the current directory), for each experiment tracked across projects.
@@ -102,6 +126,80 @@ tracker.stop()
102126

103127
The first time it will ask you to log in to Logfire. Once you log in and set the default Logfire project, the metrics will appear following the format `codecarbon_*`.
104128

129+
## BoAmps
130+
131+
[BoAmps](https://github.com/Boavizta/BoAmps) is a standardized JSON format for reporting AI and ML energy consumption.
132+
133+
### How to use it
134+
135+
Run your EmissionsTracker as usual, adding `OutputMethod.BOAMPS` to `output_methods`:
136+
137+
```python-skip
138+
from codecarbon import OfflineEmissionsTracker, OutputMethod
139+
140+
tracker = OfflineEmissionsTracker(
141+
project_name="my_project",
142+
country_iso_code="USA",
143+
output_methods=[OutputMethod.CSV, OutputMethod.BOAMPS],
144+
)
145+
tracker.start()
146+
# Your code here
147+
tracker.stop()
148+
```
149+
150+
CodeCarbon writes a final report named `boamps_report_<run_id>.json` in `output_dir`.
151+
152+
If you need to enrich the report with task metadata, datasets, or publisher information, use `BoAmpsOutput` directly through `output_handlers` or start from [examples/boamps_output.py](https://github.com/mlco2/codecarbon/blob/master/examples/boamps_output.py).
153+
154+
Sample output:
155+
```json
156+
{
157+
"header": {
158+
"formatVersion": "0.1",
159+
"formatVersionSpecificationUri": "https://github.com/Boavizta/BoAmps/tree/main/model",
160+
"reportId": "79e4408f-ec31-476f-a2c5-8ca7f53e6cc7",
161+
"reportDatetime": "2026-04-09 23:07:42"
162+
},
163+
"measures": [
164+
{
165+
"measurementMethod": "codecarbon",
166+
"version": "3.2.6",
167+
"averageUtilizationCpu": 0.6,
168+
"powerConsumption": 6.515418096322266e-05,
169+
"measurementDuration": 7.052794550996623,
170+
"measurementDateTime": "2026-04-09 23:07:42"
171+
}
172+
],
173+
"system": {
174+
"os": "Linux-6.17.0-19-generic-x86_64-with-glibc2.42"
175+
},
176+
"software": {
177+
"language": "python",
178+
"version": "3.12.12"
179+
},
180+
"infrastructure": {
181+
"infraType": "onPremise",
182+
"components": [
183+
{
184+
"componentName": "Intel(R) Core(TM) Ultra 7 265H",
185+
"componentType": "cpu",
186+
"nbComponent": 8
187+
},
188+
{
189+
"componentType": "ram",
190+
"nbComponent": 1,
191+
"memorySize": 30.052967071533203
192+
}
193+
]
194+
},
195+
"environment": {
196+
"country": "France",
197+
"latitude": 48.6,
198+
"longitude": 2.3
199+
}
200+
}
201+
```
202+
105203
## HTTP Output
106204

107205
The HTTP Output allows calling a webhook with emission data when the tracker is stopped. Use the `emissions_endpoint` parameter to specify your endpoint.

examples/boamps_output.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import multiprocessing
2+
3+
from codecarbon import EmissionsTracker, OutputMethod
4+
5+
6+
def cpu_load_task(number):
7+
a = 0
8+
for i in range(5):
9+
for i in range(int(1e6)):
10+
a = a + i**number
11+
12+
13+
tracker = EmissionsTracker(
14+
measure_power_secs=10,
15+
force_mode_cpu_load=False,
16+
log_level="debug",
17+
output_methods=[OutputMethod.BOAMPS],
18+
)
19+
try:
20+
tracker.start()
21+
with multiprocessing.Pool() as pool:
22+
# call the function for each item in parallel
23+
pool.map(cpu_load_task, [i for i in range(100)])
24+
finally:
25+
emissions = tracker.stop()
26+
27+
print(f"Emissions: {emissions} kg")
28+
print(f"BoAmps report written to ./boamps_report_{tracker.run_id}.json")

tests/test_emissions_tracker.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
track_emissions,
1919
)
2020
from codecarbon.external.geography import CloudMetadata
21+
from codecarbon.output import BoAmpsOutput, CodeCarbonAPIOutput, OutputMethod
2122
from tests.fake_modules import pynvml as fake_pynvml
2223
from tests.testdata import (
2324
GEO_METADATA_CANADA,
@@ -212,6 +213,121 @@ def raise_exception(*args, **kwargs):
212213
tracker._measure_power = raise_exception
213214
tracker.stop()
214215

216+
def test_output_methods_boamps_adds_boamps_output_handler(
217+
self,
218+
mock_cli_setup,
219+
mock_log_values,
220+
mocked_get_gpu_details,
221+
mocked_env_cloud_details,
222+
mocked_is_gpu_details_available,
223+
mocked_is_nvidia_system,
224+
):
225+
tracker = EmissionsTracker(
226+
output_dir=self.temp_path,
227+
output_handlers=[],
228+
output_methods=[OutputMethod.BOAMPS],
229+
)
230+
231+
self.assertTrue(
232+
any(
233+
isinstance(handler, BoAmpsOutput)
234+
for handler in tracker._output_handlers
235+
)
236+
)
237+
238+
def test_default_output_methods_is_csv(
239+
self,
240+
mock_cli_setup,
241+
mock_log_values,
242+
mocked_get_gpu_details,
243+
mocked_env_cloud_details,
244+
mocked_is_gpu_details_available,
245+
mocked_is_nvidia_system,
246+
):
247+
tracker = EmissionsTracker(
248+
output_dir=self.temp_path,
249+
output_handlers=[],
250+
)
251+
252+
self.assertEqual(tracker._output_methods, [OutputMethod.CSV])
253+
254+
def test_save_to_flags_map_to_output_methods_and_warn(
255+
self,
256+
mock_cli_setup,
257+
mock_log_values,
258+
mocked_get_gpu_details,
259+
mocked_env_cloud_details,
260+
mocked_is_gpu_details_available,
261+
mocked_is_nvidia_system,
262+
):
263+
with self.assertWarns(DeprecationWarning):
264+
tracker = EmissionsTracker(
265+
output_dir=self.temp_path,
266+
output_handlers=[],
267+
save_to_file=True,
268+
save_to_logger=True,
269+
)
270+
271+
self.assertIn(OutputMethod.CSV, tracker._output_methods)
272+
self.assertIn(OutputMethod.LOGGER, tracker._output_methods)
273+
self.assertNotIn(OutputMethod.API, tracker._output_methods)
274+
self.assertTrue(tracker._save_to_file)
275+
self.assertTrue(tracker._save_to_logger)
276+
self.assertFalse(tracker._save_to_api)
277+
278+
def test_output_methods_overrides_save_to_flags(
279+
self,
280+
mock_cli_setup,
281+
mock_log_values,
282+
mocked_get_gpu_details,
283+
mocked_env_cloud_details,
284+
mocked_is_gpu_details_available,
285+
mocked_is_nvidia_system,
286+
):
287+
with self.assertWarns(DeprecationWarning):
288+
tracker = EmissionsTracker(
289+
output_dir=self.temp_path,
290+
output_handlers=[],
291+
output_methods=[OutputMethod.CSV],
292+
save_to_api=True,
293+
)
294+
295+
self.assertEqual(tracker._output_methods, [OutputMethod.CSV])
296+
self.assertFalse(tracker._save_to_api)
297+
self.assertFalse(
298+
any(
299+
isinstance(handler, CodeCarbonAPIOutput)
300+
for handler in tracker._output_handlers
301+
)
302+
)
303+
304+
def test_output_methods_parsed_from_config_string(
305+
self,
306+
mock_cli_setup,
307+
mock_log_values,
308+
mocked_get_gpu_details,
309+
mocked_env_cloud_details,
310+
mocked_is_gpu_details_available,
311+
mocked_is_nvidia_system,
312+
):
313+
tracker = EmissionsTracker(
314+
output_dir=self.temp_path,
315+
output_handlers=[],
316+
output_methods="csv,boamps",
317+
)
318+
319+
self.assertEqual(
320+
tracker._output_methods,
321+
[OutputMethod.CSV, OutputMethod.BOAMPS],
322+
)
323+
boamps_handlers = [
324+
handler
325+
for handler in tracker._output_handlers
326+
if isinstance(handler, BoAmpsOutput)
327+
]
328+
self.assertEqual(len(boamps_handlers), 1)
329+
self.assertEqual(str(boamps_handlers[0]._output_dir), str(self.temp_path))
330+
215331
@responses.activate
216332
def test_decorator_ONLINE_NO_ARGS(
217333
self,
@@ -268,6 +384,35 @@ def dummy_train_model():
268384
# THEN
269385
self.verify_output_file(self.emissions_file_path, 2)
270386

387+
def test_decorator_online_passes_output_methods(
388+
self,
389+
mock_cli_setup,
390+
mock_log_values,
391+
mocked_get_gpu_details,
392+
mocked_env_cloud_details,
393+
mocked_is_gpu_details_available,
394+
mocked_is_nvidia_system,
395+
):
396+
mocked_tracker = mock.Mock()
397+
398+
with mock.patch(
399+
"codecarbon.emissions_tracker.EmissionsTracker",
400+
return_value=mocked_tracker,
401+
) as mocked_tracker_cls:
402+
403+
@track_emissions(output_methods=[OutputMethod.BOAMPS])
404+
def dummy_train_model():
405+
return 42
406+
407+
self.assertEqual(dummy_train_model(), 42)
408+
409+
self.assertEqual(
410+
mocked_tracker_cls.call_args.kwargs["output_methods"],
411+
[OutputMethod.BOAMPS],
412+
)
413+
mocked_tracker.start.assert_called_once()
414+
mocked_tracker.stop.assert_called_once()
415+
271416
def test_decorator_OFFLINE_NO_COUNTRY(
272417
self,
273418
mock_cli_setup,

0 commit comments

Comments
 (0)