codecarbon 2.4.2__tar.gz → 2.5.0__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.
- {codecarbon-2.4.2 → codecarbon-2.5.0}/.gitignore +1 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/PKG-INFO +1 -1
- codecarbon-2.5.0/codecarbon/_version.py +1 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/api_client.py +8 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/powermetrics.py +8 -0
- codecarbon-2.5.0/codecarbon/data/cloud/impact.csv +37 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/emissions_tracker.py +99 -72
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/geography.py +8 -4
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/hardware.py +3 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output.py +2 -3
- codecarbon-2.5.0/codecarbon/output_methods/base_output.py +24 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/file.py +22 -15
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/http.py +4 -4
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/logger.py +10 -4
- codecarbon-2.5.0/codecarbon/output_methods/metrics/logfire.py +79 -0
- {codecarbon-2.4.2/codecarbon/output_methods/metrics/prometheus → codecarbon-2.5.0/codecarbon/output_methods/metrics}/prometheus.py +68 -15
- codecarbon-2.4.2/codecarbon/_version.py +0 -1
- codecarbon-2.4.2/codecarbon/data/cloud/impact.csv +0 -35
- codecarbon-2.4.2/codecarbon/output_methods/base_output.py +0 -15
- codecarbon-2.4.2/codecarbon/output_methods/metrics/prometheus/metrics.py +0 -65
- codecarbon-2.4.2/codecarbon/viz/assets/__init__.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/LICENSE +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/README.md +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/__init__.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/cli/__init__.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/cli/cli_utils.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/cli/main.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/__init__.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/cloud.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/co2_signal.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/config.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/cpu.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/emissions.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/gpu.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/measure.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/rapl.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/schemas.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/units.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/util.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/canada_provinces.geojson +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/hardware/cpu_power.csv +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2016/canada_energy_mix.json +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2016/global_energy_mix-old.json +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2016/usa_emissions.json +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/01_get_world_carbon_intensity.ipynb +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/02_convert_csv_to_json.ipynb +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/03_add_eu_data.ipynb +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/eu-carbon-intensity-electricity.csv +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2023-07-07-22-40-48.png +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/carbon_intensity_per_source.json +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/global_energy_mix.json +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/our_world_in_data-2022_data.ipynb +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/world_energy_mix.csv +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/__init__.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/logger.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/scheduler.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/task.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/input.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/__init__.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/emissions_data.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/metrics/__init__.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/metrics/metric_docs.py +0 -0
- {codecarbon-2.4.2/codecarbon/output_methods/metrics/prometheus → codecarbon-2.5.0/codecarbon/viz}/__init__.py +0 -0
- {codecarbon-2.4.2/codecarbon/viz → codecarbon-2.5.0/codecarbon/viz/assets}/__init__.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/assets/car_icon.png +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/assets/house_icon.png +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/assets/tv_icon.png +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/carbonboard.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/carbonboard_on_api.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/components.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/data.py +0 -0
- {codecarbon-2.4.2 → codecarbon-2.5.0}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.5.0"
|
|
@@ -52,6 +52,14 @@ class ApiClient: # (AsyncClient)
|
|
|
52
52
|
self.conf = conf
|
|
53
53
|
if self.experiment_id is not None:
|
|
54
54
|
self._create_run(self.experiment_id)
|
|
55
|
+
import warnings
|
|
56
|
+
|
|
57
|
+
# FIXME: remove this warning in the future, once the release is created
|
|
58
|
+
warnings.warn(
|
|
59
|
+
"Beta API will be reworked, and some features will be removed. If you have data persisted through the API, please be warned that it will be erased with the next API release",
|
|
60
|
+
DeprecationWarning,
|
|
61
|
+
stacklevel=2,
|
|
62
|
+
)
|
|
55
63
|
|
|
56
64
|
def add_emission(self, carbon_emission: dict):
|
|
57
65
|
assert self.experiment_id is not None
|
|
@@ -25,6 +25,14 @@ def is_powermetrics_available():
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def _has_powermetrics_sudo():
|
|
28
|
+
if shutil.which("sudo") is None:
|
|
29
|
+
logger.debug("sudo not available, we won't use Apple PowerMetrics.")
|
|
30
|
+
return False
|
|
31
|
+
if shutil.which("powermetrics") is None:
|
|
32
|
+
logger.info(
|
|
33
|
+
"Apple PowerMetrics not available. Please install it if you are using an Apple product."
|
|
34
|
+
)
|
|
35
|
+
return False
|
|
28
36
|
process = subprocess.Popen(
|
|
29
37
|
[
|
|
30
38
|
"sudo",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
provider,providerName,offsetRatio,region,regionName,country_name,countryIsoCode,state,city,impact,source,comment
|
|
2
|
+
gcp,Google Cloud Platform,100,asia-east1,,Taiwan,TWN,,Changhua County,453,https://cloud.google.com/sustainability/region-carbon#data,
|
|
3
|
+
gcp,Google Cloud Platform,100,asia-east2,,China,CHN,,Hong Kong,360,https://cloud.google.com/sustainability/region-carbon#data,
|
|
4
|
+
gcp,Google Cloud Platform,100,asia-northeast1,,Japan,JPN,,Tokyo,463,https://cloud.google.com/sustainability/region-carbon#data,
|
|
5
|
+
gcp,Google Cloud Platform,100,asia-northeast2,,Japan,JPN,,Osaka,383,https://cloud.google.com/sustainability/region-carbon#data,
|
|
6
|
+
gcp,Google Cloud Platform,100,asia-northeast3,,Korea,KOR,South Korea,Seoul,425,https://cloud.google.com/sustainability/region-carbon#data,
|
|
7
|
+
gcp,Google Cloud Platform,100,asia-south1,,India,IND,,Mumbai,555,https://cloud.google.com/sustainability/region-carbon#data,
|
|
8
|
+
gcp,Google Cloud Platform,100,asia-south2,,India,IND,,Delhi,632,https://cloud.google.com/sustainability/region-carbon#data,
|
|
9
|
+
gcp,Google Cloud Platform,100,asia-southeast1,,Singapore,SGP,,Jurong West,372,https://cloud.google.com/sustainability/region-carbon#data,
|
|
10
|
+
gcp,Google Cloud Platform,100,asia-southeast2,,Indonesia,IDN,,Jakarta,580,https://cloud.google.com/sustainability/region-carbon#data,
|
|
11
|
+
gcp,Google Cloud Platform,100,australia-southeast1,,Australia,AUS,,Sydney,538,https://cloud.google.com/sustainability/region-carbon#data,
|
|
12
|
+
gcp,Google Cloud Platform,100,australia-southeast2,,Australia,AUS,,Melbourne,490,https://cloud.google.com/sustainability/region-carbon#data,
|
|
13
|
+
gcp,Google Cloud Platform,100,europe-central2,,Poland,POL,,Warsaw,738,https://cloud.google.com/sustainability/region-carbon#data,
|
|
14
|
+
gcp,Google Cloud Platform,100,europe-north1,,Finland,FIN,,Hamina,112,https://cloud.google.com/sustainability/region-carbon#data,
|
|
15
|
+
gcp,Google Cloud Platform,100,europe-southwest1,,Spain,ESP,,Madrid,160,https://cloud.google.com/sustainability/region-carbon#data,
|
|
16
|
+
gcp,Google Cloud Platform,100,europe-west1,,Belgium,BEL,,St. Ghislain,123,https://cloud.google.com/sustainability/region-carbon#data,
|
|
17
|
+
gcp,Google Cloud Platform,100,europe-west2,,United Kingdom,GBR,England,London,166,https://cloud.google.com/sustainability/region-carbon#data,
|
|
18
|
+
gcp,Google Cloud Platform,100,europe-west3,,Germany,DEU,,Frankfurt,413,https://cloud.google.com/sustainability/region-carbon#data,
|
|
19
|
+
gcp,Google Cloud Platform,100,europe-west4,,Netherlands,NLD,,Eemshaven,317,https://cloud.google.com/sustainability/region-carbon#data,
|
|
20
|
+
gcp,Google Cloud Platform,100,europe-west6,,Switzerland,CHE,,Zurich,118,https://cloud.google.com/sustainability/region-carbon#data,
|
|
21
|
+
gcp,Google Cloud Platform,100,europe-west8,,Italy,ITA,,Milan,323,https://cloud.google.com/sustainability/region-carbon#data,
|
|
22
|
+
gcp,Google Cloud Platform,100,europe-west9,,France,FRA,,Paris,71,https://cloud.google.com/sustainability/region-carbon#data,
|
|
23
|
+
gcp,Google Cloud Platform,100,europe-west12,,Italy,ITA,,Turin,323,https://cloud.google.com/sustainability/region-carbon#data,
|
|
24
|
+
gcp,Google Cloud Platform,100,me-west1,,Israel,ISR,,Tel Aviv,476,https://cloud.google.com/sustainability/region-carbon#data,
|
|
25
|
+
gcp,Google Cloud Platform,100,northamerica-northeast1,,Canada,CAN,Quebec,Montreal,0,https://cloud.google.com/sustainability/region-carbon#data,
|
|
26
|
+
gcp,Google Cloud Platform,100,northamerica-northeast2,,Canada,CAN,,Toronto,36,https://cloud.google.com/sustainability/region-carbon#data,
|
|
27
|
+
gcp,Google Cloud Platform,100,southamerica-east1,,Brazil,BRA,,Sao Paulo,65,https://cloud.google.com/sustainability/region-carbon#data,
|
|
28
|
+
gcp,Google Cloud Platform,100,southamerica-west1,,Chili,CHL,,Santiago,165,https://cloud.google.com/sustainability/region-carbon#data,
|
|
29
|
+
gcp,Google Cloud Platform,100,us-central1,,USA,USA,Iowa,Council Bluffs,445,https://cloud.google.com/sustainability/region-carbon#data,
|
|
30
|
+
gcp,Google Cloud Platform,100,us-east1,,USA,USA,South Carolina,Moncks Corner,532,https://cloud.google.com/sustainability/region-carbon#data,
|
|
31
|
+
gcp,Google Cloud Platform,100,us-east4,,USA,USA,Northern Virginia,Ashburn,354,https://cloud.google.com/sustainability/region-carbon#data,
|
|
32
|
+
gcp,Google Cloud Platform,100,us-east5,,USA,USA,,Columbus,354,https://cloud.google.com/sustainability/region-carbon#data,
|
|
33
|
+
gcp,Google Cloud Platform,100,us-south1,,USA,USA,,Dallas,342,https://cloud.google.com/sustainability/region-carbon#data,
|
|
34
|
+
gcp,Google Cloud Platform,100,us-west1,,USA,USA,Oregon,The Dalles,67,https://cloud.google.com/sustainability/region-carbon#data,
|
|
35
|
+
gcp,Google Cloud Platform,100,us-west2,,USA,USA,California,Los Angeles,202,https://cloud.google.com/sustainability/region-carbon#data,
|
|
36
|
+
gcp,Google Cloud Platform,100,us-west3,,USA,USA,Utah,Salt Lake City,606,https://cloud.google.com/sustainability/region-carbon#data,
|
|
37
|
+
gcp,Google Cloud Platform,100,us-west4,,USA,USA,Nevada,Las Vegas,396,https://cloud.google.com/sustainability/region-carbon#data,
|
|
@@ -4,7 +4,6 @@ OfflineEmissionsTracker and @track_emissions
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import dataclasses
|
|
7
|
-
import os
|
|
8
7
|
import platform
|
|
9
8
|
import time
|
|
10
9
|
import uuid
|
|
@@ -32,6 +31,7 @@ from codecarbon.output import (
|
|
|
32
31
|
EmissionsData,
|
|
33
32
|
FileOutput,
|
|
34
33
|
HTTPOutput,
|
|
34
|
+
LogfireOutput,
|
|
35
35
|
LoggerOutput,
|
|
36
36
|
PrometheusOutput,
|
|
37
37
|
)
|
|
@@ -149,7 +149,9 @@ class BaseEmissionsTracker(ABC):
|
|
|
149
149
|
save_to_logger: Optional[bool] = _sentinel,
|
|
150
150
|
logging_logger: Optional[LoggerOutput] = _sentinel,
|
|
151
151
|
save_to_prometheus: Optional[bool] = _sentinel,
|
|
152
|
+
save_to_logfire: Optional[bool] = _sentinel,
|
|
152
153
|
prometheus_url: Optional[str] = _sentinel,
|
|
154
|
+
output_handlers: Optional[List[BaseOutput]] = _sentinel,
|
|
153
155
|
gpu_ids: Optional[List] = _sentinel,
|
|
154
156
|
emissions_endpoint: Optional[str] = _sentinel,
|
|
155
157
|
experiment_id: Optional[str] = _sentinel,
|
|
@@ -187,6 +189,8 @@ class BaseEmissionsTracker(ABC):
|
|
|
187
189
|
or a Google Cloud logger.
|
|
188
190
|
:param save_to_prometheus: Indicates if the emission artifacts should be
|
|
189
191
|
pushed to prometheus, defaults to False.
|
|
192
|
+
:param save_to_logfire: Indicates if the emission artifacts should be written
|
|
193
|
+
to a logfire observability platform, defaults to False.
|
|
190
194
|
:param prometheus_url: url of the prometheus server, defaults to `localhost:9091`.
|
|
191
195
|
:param gpu_ids: User-specified known gpu ids to track.
|
|
192
196
|
Defaults to None, which means that all available gpus will be tracked.
|
|
@@ -234,7 +238,9 @@ class BaseEmissionsTracker(ABC):
|
|
|
234
238
|
self._set_from_conf(save_to_logger, "save_to_logger", False, bool)
|
|
235
239
|
self._set_from_conf(logging_logger, "logging_logger")
|
|
236
240
|
self._set_from_conf(save_to_prometheus, "save_to_prometheus", False, bool)
|
|
241
|
+
self._set_from_conf(save_to_logfire, "save_to_logfire", False, bool)
|
|
237
242
|
self._set_from_conf(prometheus_url, "prometheus_url", "localhost:9091")
|
|
243
|
+
self._set_from_conf(output_handlers, "output_handlers", [])
|
|
238
244
|
self._set_from_conf(tracking_mode, "tracking_mode", "machine")
|
|
239
245
|
self._set_from_conf(on_csv_write, "on_csv_write", "append")
|
|
240
246
|
self._set_from_conf(logger_preamble, "logger_preamble", "")
|
|
@@ -257,8 +263,6 @@ class BaseEmissionsTracker(ABC):
|
|
|
257
263
|
self._cpu_power: Power = Power.from_watts(watts=0)
|
|
258
264
|
self._gpu_power: Power = Power.from_watts(watts=0)
|
|
259
265
|
self._ram_power: Power = Power.from_watts(watts=0)
|
|
260
|
-
self._cc_api__out = None
|
|
261
|
-
self._cc_prometheus_out = None
|
|
262
266
|
self._measure_occurrence: int = 0
|
|
263
267
|
self._cloud = None
|
|
264
268
|
self._previous_emissions = None
|
|
@@ -271,18 +275,19 @@ class BaseEmissionsTracker(ABC):
|
|
|
271
275
|
self._tasks: Dict[str, Task] = {}
|
|
272
276
|
self._active_task: Optional[str] = None
|
|
273
277
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
isinstance(self._gpu_ids,
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
278
|
+
if self._gpu_ids:
|
|
279
|
+
# If _gpu_ids is a string or a list of int, parse it to a list of ints
|
|
280
|
+
if isinstance(self._gpu_ids, str) or (
|
|
281
|
+
isinstance(self._gpu_ids, list)
|
|
282
|
+
and all(isinstance(gpu_id, int) for gpu_id in self._gpu_ids)
|
|
283
|
+
):
|
|
284
|
+
self._gpu_ids: List[int] = parse_gpu_ids(self._gpu_ids)
|
|
285
|
+
self._conf["gpu_ids"] = self._gpu_ids
|
|
286
|
+
self._conf["gpu_count"] = len(self._gpu_ids)
|
|
287
|
+
else:
|
|
288
|
+
logger.warning(
|
|
289
|
+
"Invalid gpu_ids format. Expected a string or a list of ints."
|
|
290
|
+
)
|
|
286
291
|
|
|
287
292
|
logger.info("[setup] RAM Tracking...")
|
|
288
293
|
ram = RAM(tracking_mode=self._tracking_mode)
|
|
@@ -399,38 +404,38 @@ class BaseEmissionsTracker(ABC):
|
|
|
399
404
|
"""
|
|
400
405
|
Prepare the different output methods
|
|
401
406
|
"""
|
|
402
|
-
self.persistence_objs: List[BaseOutput] = list()
|
|
403
|
-
|
|
404
407
|
if self._save_to_file:
|
|
405
|
-
self.
|
|
408
|
+
self._output_handlers.append(
|
|
406
409
|
FileOutput(
|
|
407
|
-
|
|
410
|
+
self._output_file,
|
|
411
|
+
self._output_dir,
|
|
408
412
|
self._on_csv_write,
|
|
409
413
|
)
|
|
410
414
|
)
|
|
411
415
|
|
|
412
416
|
if self._save_to_logger:
|
|
413
|
-
self.
|
|
417
|
+
self._output_handlers.append(self._logging_logger)
|
|
414
418
|
|
|
415
419
|
if self._emissions_endpoint:
|
|
416
|
-
self.
|
|
420
|
+
self._output_handlers.append(HTTPOutput(self._emissions_endpoint))
|
|
417
421
|
|
|
418
422
|
if self._save_to_api:
|
|
419
|
-
|
|
423
|
+
cc_api__out = CodeCarbonAPIOutput(
|
|
420
424
|
endpoint_url=self._api_endpoint,
|
|
421
425
|
experiment_id=self._experiment_id,
|
|
422
426
|
api_key=api_key,
|
|
423
427
|
conf=self._conf,
|
|
424
428
|
)
|
|
425
|
-
self.run_id =
|
|
426
|
-
self.
|
|
427
|
-
|
|
429
|
+
self.run_id = cc_api__out.run_id
|
|
430
|
+
self._output_handlers.append(cc_api__out)
|
|
428
431
|
else:
|
|
429
432
|
self.run_id = uuid.uuid4()
|
|
430
433
|
|
|
431
434
|
if self._save_to_prometheus:
|
|
432
|
-
self.
|
|
433
|
-
|
|
435
|
+
self._output_handlers.append(PrometheusOutput(self._prometheus_url))
|
|
436
|
+
|
|
437
|
+
if self._save_to_logfire:
|
|
438
|
+
self._output_handlers.append(LogfireOutput())
|
|
434
439
|
|
|
435
440
|
def service_shutdown(self, signum, frame):
|
|
436
441
|
print("Caught signal %d" % signum)
|
|
@@ -476,7 +481,8 @@ class BaseEmissionsTracker(ABC):
|
|
|
476
481
|
# Read initial energy for hardware
|
|
477
482
|
for hardware in self._hardware:
|
|
478
483
|
hardware.start()
|
|
479
|
-
_ = self._prepare_emissions_data(
|
|
484
|
+
_ = self._prepare_emissions_data()
|
|
485
|
+
_ = self._compute_emissions_delta(_)
|
|
480
486
|
|
|
481
487
|
self._tasks.update(
|
|
482
488
|
{
|
|
@@ -496,13 +502,14 @@ class BaseEmissionsTracker(ABC):
|
|
|
496
502
|
task_name = task_name if task_name else self._active_task
|
|
497
503
|
self._measure_power_and_energy()
|
|
498
504
|
|
|
499
|
-
emissions_data = self._prepare_emissions_data(
|
|
505
|
+
emissions_data = self._prepare_emissions_data()
|
|
506
|
+
emissions_data_delta = self._compute_emissions_delta(emissions_data)
|
|
500
507
|
|
|
501
508
|
task_duration = Time.from_seconds(
|
|
502
509
|
time.time() - self._tasks[task_name].start_time
|
|
503
510
|
)
|
|
504
511
|
|
|
505
|
-
task_emission_data =
|
|
512
|
+
task_emission_data = emissions_data_delta
|
|
506
513
|
task_emission_data.duration = task_duration.seconds
|
|
507
514
|
self._tasks[task_name].emissions_data = task_emission_data
|
|
508
515
|
self._tasks[task_name].is_active = False
|
|
@@ -525,7 +532,11 @@ class BaseEmissionsTracker(ABC):
|
|
|
525
532
|
self._measure_power_and_energy()
|
|
526
533
|
|
|
527
534
|
emissions_data = self._prepare_emissions_data()
|
|
528
|
-
self.
|
|
535
|
+
emissions_data_delta = self._compute_emissions_delta(emissions_data)
|
|
536
|
+
|
|
537
|
+
self._persist_data(
|
|
538
|
+
total_emissions=emissions_data, delta_emissions=emissions_data_delta
|
|
539
|
+
)
|
|
529
540
|
|
|
530
541
|
return emissions_data.emissions
|
|
531
542
|
|
|
@@ -553,29 +564,34 @@ class BaseEmissionsTracker(ABC):
|
|
|
553
564
|
self._measure_power_and_energy()
|
|
554
565
|
|
|
555
566
|
emissions_data = self._prepare_emissions_data()
|
|
567
|
+
emissions_data_delta = self._compute_emissions_delta(emissions_data)
|
|
556
568
|
|
|
557
|
-
self._persist_data(
|
|
569
|
+
self._persist_data(
|
|
570
|
+
total_emissions=emissions_data,
|
|
571
|
+
delta_emissions=emissions_data_delta,
|
|
572
|
+
experiment_name=self._experiment_name,
|
|
573
|
+
)
|
|
558
574
|
|
|
559
575
|
self.final_emissions_data = emissions_data
|
|
560
576
|
self.final_emissions = emissions_data.emissions
|
|
561
577
|
return emissions_data.emissions
|
|
562
578
|
|
|
563
|
-
def _persist_data(
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
579
|
+
def _persist_data(
|
|
580
|
+
self,
|
|
581
|
+
total_emissions: EmissionsData,
|
|
582
|
+
delta_emissions: EmissionsData,
|
|
583
|
+
experiment_name=None,
|
|
584
|
+
):
|
|
585
|
+
task_emissions_data = []
|
|
586
|
+
for task in self._tasks:
|
|
587
|
+
task_emissions_data.append(self._tasks[task].out())
|
|
588
|
+
|
|
589
|
+
for handler in self._output_handlers:
|
|
590
|
+
handler.out(total_emissions, delta_emissions)
|
|
591
|
+
if len(task_emissions_data) > 0:
|
|
592
|
+
handler.task_out(task_emissions_data, experiment_name)
|
|
577
593
|
|
|
578
|
-
def _prepare_emissions_data(self
|
|
594
|
+
def _prepare_emissions_data(self) -> EmissionsData:
|
|
579
595
|
"""
|
|
580
596
|
:delta: If 'True', return only the delta comsumption since the last call.
|
|
581
597
|
"""
|
|
@@ -635,21 +651,23 @@ class BaseEmissionsTracker(ABC):
|
|
|
635
651
|
tracking_mode=self._conf.get("tracking_mode"),
|
|
636
652
|
pue=self._pue,
|
|
637
653
|
)
|
|
638
|
-
if delta:
|
|
639
|
-
if self._previous_emissions is None:
|
|
640
|
-
self._previous_emissions = total_emissions
|
|
641
|
-
else:
|
|
642
|
-
# Create a copy
|
|
643
|
-
delta_emissions = dataclasses.replace(total_emissions)
|
|
644
|
-
# Compute emissions rate from delta
|
|
645
|
-
delta_emissions.compute_delta_emission(self._previous_emissions)
|
|
646
|
-
# TODO : find a way to store _previous_emissions only when
|
|
647
|
-
# TODO : the API call succeeded
|
|
648
|
-
self._previous_emissions = total_emissions
|
|
649
|
-
total_emissions = delta_emissions
|
|
650
654
|
logger.debug(total_emissions)
|
|
651
655
|
return total_emissions
|
|
652
656
|
|
|
657
|
+
def _compute_emissions_delta(self, total_emissions: EmissionsData) -> EmissionsData:
|
|
658
|
+
delta_emissions: EmissionsData = total_emissions
|
|
659
|
+
if self._previous_emissions is None:
|
|
660
|
+
self._previous_emissions = total_emissions
|
|
661
|
+
else:
|
|
662
|
+
# Create a copy
|
|
663
|
+
delta_emissions = dataclasses.replace(total_emissions)
|
|
664
|
+
# Compute emissions rate from delta
|
|
665
|
+
delta_emissions.compute_delta_emission(self._previous_emissions)
|
|
666
|
+
# TODO : find a way to store _previous_emissions only when
|
|
667
|
+
# TODO : the API call succeeded
|
|
668
|
+
self._previous_emissions = total_emissions
|
|
669
|
+
return delta_emissions
|
|
670
|
+
|
|
653
671
|
@abstractmethod
|
|
654
672
|
def _get_geo_metadata(self) -> GeoMetadata:
|
|
655
673
|
"""
|
|
@@ -740,20 +758,21 @@ class BaseEmissionsTracker(ABC):
|
|
|
740
758
|
self._do_measurements()
|
|
741
759
|
self._last_measured_time = time.time()
|
|
742
760
|
self._measure_occurrence += 1
|
|
761
|
+
# Special case: metrics and api calls are sent every `api_call_interval` measures
|
|
743
762
|
if (
|
|
744
|
-
self.
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
763
|
+
self._api_call_interval != -1
|
|
764
|
+
and len(self._output_handlers) > 0
|
|
765
|
+
and self._measure_occurrence >= self._api_call_interval
|
|
766
|
+
):
|
|
767
|
+
emissions = self._prepare_emissions_data()
|
|
768
|
+
emissions_delta = self._compute_emissions_delta(emissions)
|
|
769
|
+
logger.info(
|
|
770
|
+
f"{emissions_delta.emissions_rate * 1000:.6f} g.CO2eq/s mean an estimation of "
|
|
771
|
+
+ f"{emissions_delta.emissions_rate * 3600 * 24 * 365:,} kg.CO2eq/year"
|
|
772
|
+
)
|
|
773
|
+
for handler in self._output_handlers:
|
|
774
|
+
handler.live_out(emissions, emissions_delta)
|
|
775
|
+
self._measure_occurrence = 0
|
|
757
776
|
logger.debug(f"last_duration={last_duration}\n------------------------")
|
|
758
777
|
|
|
759
778
|
def __enter__(self):
|
|
@@ -922,7 +941,9 @@ def track_emissions(
|
|
|
922
941
|
save_to_api: Optional[bool] = _sentinel,
|
|
923
942
|
save_to_logger: Optional[bool] = _sentinel,
|
|
924
943
|
save_to_prometheus: Optional[bool] = _sentinel,
|
|
944
|
+
save_to_logfire: Optional[bool] = _sentinel,
|
|
925
945
|
prometheus_url: Optional[str] = _sentinel,
|
|
946
|
+
output_handlers: Optional[List[BaseOutput]] = _sentinel,
|
|
926
947
|
logging_logger: Optional[LoggerOutput] = _sentinel,
|
|
927
948
|
offline: Optional[bool] = _sentinel,
|
|
928
949
|
emissions_endpoint: Optional[str] = _sentinel,
|
|
@@ -956,6 +977,8 @@ def track_emissions(
|
|
|
956
977
|
to a dedicated logger, defaults to False.
|
|
957
978
|
:param save_to_prometheus: Indicates if the emission artifacts should be
|
|
958
979
|
pushed to prometheus, defaults to False.
|
|
980
|
+
:param save_to_logfire: Indicates if the emission artifacts should be
|
|
981
|
+
pushed to logfire, defaults to False.
|
|
959
982
|
:param prometheus_url: url of the prometheus server, defaults to `localhost:9091`.
|
|
960
983
|
:param logging_logger: LoggerOutput object encapsulating a logging.logger
|
|
961
984
|
or a Google Cloud logger.
|
|
@@ -1000,7 +1023,9 @@ def track_emissions(
|
|
|
1000
1023
|
save_to_file=save_to_file,
|
|
1001
1024
|
save_to_logger=save_to_logger,
|
|
1002
1025
|
save_to_prometheus=save_to_prometheus,
|
|
1026
|
+
save_to_logfire=save_to_logfire,
|
|
1003
1027
|
prometheus_url=prometheus_url,
|
|
1028
|
+
output_handlers=output_handlers,
|
|
1004
1029
|
logging_logger=logging_logger,
|
|
1005
1030
|
country_iso_code=country_iso_code,
|
|
1006
1031
|
region=region,
|
|
@@ -1021,7 +1046,9 @@ def track_emissions(
|
|
|
1021
1046
|
save_to_file=save_to_file,
|
|
1022
1047
|
save_to_logger=save_to_logger,
|
|
1023
1048
|
save_to_prometheus=save_to_prometheus,
|
|
1049
|
+
save_to_logfire=save_to_logfire,
|
|
1024
1050
|
prometheus_url=prometheus_url,
|
|
1051
|
+
output_handlers=output_handlers,
|
|
1025
1052
|
logging_logger=logging_logger,
|
|
1026
1053
|
gpu_ids=gpu_ids,
|
|
1027
1054
|
log_level=log_level,
|
|
@@ -32,18 +32,22 @@ class CloudMetadata:
|
|
|
32
32
|
return re.search(google_region_regex, zone).group(0)
|
|
33
33
|
|
|
34
34
|
extract_region_for_provider: Dict[str, Callable] = {
|
|
35
|
-
"aws": lambda x: x["metadata"]
|
|
36
|
-
"azure": lambda x: x["metadata"]["compute"]
|
|
37
|
-
"gcp": lambda x: extract_gcp_region(x["metadata"]
|
|
35
|
+
"aws": lambda x: x["metadata"].get("region"),
|
|
36
|
+
"azure": lambda x: x["metadata"]["compute"].get("location"),
|
|
37
|
+
"gcp": lambda x: extract_gcp_region(x["metadata"].get("zone")),
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
cloud_metadata: Dict = get_env_cloud_details()
|
|
41
41
|
|
|
42
|
-
if cloud_metadata is None:
|
|
42
|
+
if cloud_metadata is None or cloud_metadata["metadata"] == {}:
|
|
43
43
|
return cls(provider=None, region=None)
|
|
44
44
|
|
|
45
45
|
provider: str = cloud_metadata["provider"].lower()
|
|
46
46
|
region: str = extract_region_for_provider.get(provider)(cloud_metadata)
|
|
47
|
+
if region is None:
|
|
48
|
+
logger.warning(
|
|
49
|
+
f"Cloud provider '{provider}' detected, but unable to read region. Using country value instead."
|
|
50
|
+
)
|
|
47
51
|
if provider in ["aws", "azure"]:
|
|
48
52
|
logger.warning(
|
|
49
53
|
f"Cloud provider '{provider}' do not publish electricity carbon intensity. Using country value instead."
|
|
@@ -308,6 +308,9 @@ class RAM(BaseHardware):
|
|
|
308
308
|
|
|
309
309
|
def _parse_scontrol(self, scontrol_str):
|
|
310
310
|
mem_matches = re.findall(r"AllocTRES=.*?,mem=(\d+[A-Z])", scontrol_str)
|
|
311
|
+
if len(mem_matches) == 0:
|
|
312
|
+
# Try with TRES, see https://github.com/mlco2/codecarbon/issues/569#issuecomment-2167706145
|
|
313
|
+
mem_matches = re.findall(r"TRES=.*?,mem=(\d+[A-Z])", scontrol_str)
|
|
311
314
|
if len(mem_matches) == 0:
|
|
312
315
|
logger.warning(
|
|
313
316
|
"Could not find mem= after running `scontrol show job $SLURM_JOB_ID` "
|
|
@@ -21,8 +21,7 @@ from codecarbon.output_methods.logger import ( # noqa: F401
|
|
|
21
21
|
GoogleCloudLoggerOutput,
|
|
22
22
|
LoggerOutput,
|
|
23
23
|
)
|
|
24
|
+
from codecarbon.output_methods.metrics.logfire import LogfireOutput # noqa: F401
|
|
24
25
|
|
|
25
26
|
# output is sent to metrics
|
|
26
|
-
from codecarbon.output_methods.metrics.prometheus
|
|
27
|
-
PrometheusOutput,
|
|
28
|
-
)
|
|
27
|
+
from codecarbon.output_methods.metrics.prometheus import PrometheusOutput # noqa: F401
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from codecarbon.output_methods.emissions_data import EmissionsData, TaskEmissionsData
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseOutput:
|
|
7
|
+
"""
|
|
8
|
+
An abstract class defining possible contracts for an output strategy, a strategy implementation can save emissions
|
|
9
|
+
data to a file, posting to Json Box, saving to a database, sending a Slack message etc.
|
|
10
|
+
Each method is responsible for a different part of the EmissionsData lifecycle:
|
|
11
|
+
- `out` is used by termination calls such as emissions_tracker.flush and emissions_tracker.stop
|
|
12
|
+
- `live_out` is used by live measurement events, e.g. the iterative update of prometheus metrics
|
|
13
|
+
- `task_out` is used by terminate calls such as emissions_tracker.flush and emissions_tracker.stop, but uses
|
|
14
|
+
emissions segregated by task
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def out(self, total: EmissionsData, delta: EmissionsData):
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def live_out(self, total: EmissionsData, delta: EmissionsData):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
def task_out(self, data: List[TaskEmissionsData], experiment_name: str):
|
|
24
|
+
pass
|
|
@@ -15,14 +15,21 @@ class FileOutput(BaseOutput):
|
|
|
15
15
|
Saves experiment artifacts to a file
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
def __init__(
|
|
18
|
+
def __init__(
|
|
19
|
+
self, output_file_name: str, output_dir: str, on_csv_write: str = "append"
|
|
20
|
+
):
|
|
19
21
|
if on_csv_write not in {"append", "update"}:
|
|
20
22
|
raise ValueError(
|
|
21
23
|
f"Unknown `on_csv_write` value: {on_csv_write}"
|
|
22
24
|
+ " (should be one of 'append' or 'update'"
|
|
23
25
|
)
|
|
26
|
+
self.output_file_name: str = output_file_name
|
|
27
|
+
self.output_dir: str = output_dir
|
|
24
28
|
self.on_csv_write: str = on_csv_write
|
|
25
|
-
self.save_file_path
|
|
29
|
+
self.save_file_path = os.path.join(self.output_dir, self.output_file_name)
|
|
30
|
+
logger.info(
|
|
31
|
+
f"Saving emissions data to file {os.path.abspath(self.save_file_path)}"
|
|
32
|
+
)
|
|
26
33
|
|
|
27
34
|
def has_valid_headers(self, data: EmissionsData):
|
|
28
35
|
with open(self.save_file_path) as csv_file:
|
|
@@ -31,42 +38,42 @@ class FileOutput(BaseOutput):
|
|
|
31
38
|
list_of_column_names = list(dict_from_csv.keys())
|
|
32
39
|
return list(data.values.keys()) == list_of_column_names
|
|
33
40
|
|
|
34
|
-
def out(self,
|
|
41
|
+
def out(self, total: EmissionsData, delta: EmissionsData):
|
|
35
42
|
file_exists: bool = os.path.isfile(self.save_file_path)
|
|
36
|
-
if file_exists and not self.has_valid_headers(
|
|
43
|
+
if file_exists and not self.has_valid_headers(total):
|
|
37
44
|
logger.info("Backing up old emission file")
|
|
38
45
|
backup(self.save_file_path)
|
|
39
46
|
file_exists = False
|
|
40
47
|
|
|
41
48
|
if not file_exists:
|
|
42
|
-
df = pd.DataFrame(columns=
|
|
43
|
-
df = pd.concat([df, pd.DataFrame.from_records([dict(
|
|
49
|
+
df = pd.DataFrame(columns=total.values.keys())
|
|
50
|
+
df = pd.concat([df, pd.DataFrame.from_records([dict(total.values)])])
|
|
44
51
|
elif self.on_csv_write == "append":
|
|
45
52
|
df = pd.read_csv(self.save_file_path)
|
|
46
|
-
df = pd.concat([df, pd.DataFrame.from_records([dict(
|
|
53
|
+
df = pd.concat([df, pd.DataFrame.from_records([dict(total.values)])])
|
|
47
54
|
else:
|
|
48
55
|
df = pd.read_csv(self.save_file_path)
|
|
49
|
-
df_run = df.loc[df.run_id ==
|
|
56
|
+
df_run = df.loc[df.run_id == total.run_id]
|
|
50
57
|
if len(df_run) < 1:
|
|
51
|
-
df = pd.concat([df, pd.DataFrame.from_records([dict(
|
|
58
|
+
df = pd.concat([df, pd.DataFrame.from_records([dict(total.values)])])
|
|
52
59
|
elif len(df_run) > 1:
|
|
53
60
|
logger.warning(
|
|
54
61
|
f"CSV contains more than 1 ({len(df_run)})"
|
|
55
|
-
+ f" rows with current run ID ({
|
|
62
|
+
+ f" rows with current run ID ({total.run_id})."
|
|
56
63
|
+ "Appending instead of updating."
|
|
57
64
|
)
|
|
58
|
-
df = pd.concat([df, pd.DataFrame.from_records([dict(
|
|
65
|
+
df = pd.concat([df, pd.DataFrame.from_records([dict(total.values)])])
|
|
59
66
|
else:
|
|
60
|
-
df.at[df.run_id ==
|
|
61
|
-
|
|
67
|
+
df.at[df.run_id == total.run_id, total.values.keys()] = (
|
|
68
|
+
total.values.values()
|
|
62
69
|
)
|
|
63
70
|
|
|
64
71
|
df.to_csv(self.save_file_path, index=False)
|
|
65
72
|
|
|
66
|
-
def task_out(self, data: List[TaskEmissionsData], experiment_name: str
|
|
73
|
+
def task_out(self, data: List[TaskEmissionsData], experiment_name: str):
|
|
67
74
|
run_id = data[0].run_id
|
|
68
75
|
save_task_file_path = os.path.join(
|
|
69
|
-
output_dir, "emissions_" + experiment_name + "_" + run_id + ".csv"
|
|
76
|
+
self.output_dir, "emissions_" + experiment_name + "_" + run_id + ".csv"
|
|
70
77
|
)
|
|
71
78
|
df = pd.DataFrame(columns=data[0].values.keys())
|
|
72
79
|
df = pd.concat(
|
|
@@ -19,9 +19,9 @@ class HTTPOutput(BaseOutput):
|
|
|
19
19
|
def __init__(self, endpoint_url: str):
|
|
20
20
|
self.endpoint_url: str = endpoint_url
|
|
21
21
|
|
|
22
|
-
def out(self,
|
|
22
|
+
def out(self, total: EmissionsData, delta: EmissionsData):
|
|
23
23
|
try:
|
|
24
|
-
payload = dataclasses.asdict(
|
|
24
|
+
payload = dataclasses.asdict(total)
|
|
25
25
|
payload["user"] = getpass.getuser()
|
|
26
26
|
resp = requests.post(self.endpoint_url, json=payload, timeout=10)
|
|
27
27
|
if resp.status_code != 201:
|
|
@@ -50,8 +50,8 @@ class CodeCarbonAPIOutput(BaseOutput):
|
|
|
50
50
|
)
|
|
51
51
|
self.run_id = self.api.run_id
|
|
52
52
|
|
|
53
|
-
def out(self,
|
|
53
|
+
def out(self, total: EmissionsData, delta: EmissionsData):
|
|
54
54
|
try:
|
|
55
|
-
self.api.add_emission(dataclasses.asdict(
|
|
55
|
+
self.api.add_emission(dataclasses.asdict(delta))
|
|
56
56
|
except Exception as e:
|
|
57
57
|
logger.error(e, exc_info=True)
|
|
@@ -16,22 +16,28 @@ class LoggerOutput(BaseOutput):
|
|
|
16
16
|
self.logger = logger
|
|
17
17
|
self.logging_severity = severity
|
|
18
18
|
|
|
19
|
-
def out(self,
|
|
19
|
+
def out(self, total: EmissionsData, delta: EmissionsData):
|
|
20
20
|
try:
|
|
21
|
-
payload = dataclasses.asdict(
|
|
21
|
+
payload = dataclasses.asdict(total)
|
|
22
22
|
self.logger.log(self.logging_severity, msg=json.dumps(payload))
|
|
23
23
|
except Exception as e:
|
|
24
24
|
logger.error(e, exc_info=True)
|
|
25
25
|
|
|
26
|
+
def live_out(self, total: EmissionsData, delta: EmissionsData):
|
|
27
|
+
self.out(total, delta)
|
|
28
|
+
|
|
26
29
|
|
|
27
30
|
class GoogleCloudLoggerOutput(LoggerOutput):
|
|
28
31
|
"""
|
|
29
32
|
Send emissions data to GCP Cloud Logging
|
|
30
33
|
"""
|
|
31
34
|
|
|
32
|
-
def out(self,
|
|
35
|
+
def out(self, total: EmissionsData, delta: EmissionsData):
|
|
33
36
|
try:
|
|
34
|
-
payload = dataclasses.asdict(
|
|
37
|
+
payload = dataclasses.asdict(total)
|
|
35
38
|
self.logger.log_struct(payload, severity=self.logging_severity)
|
|
36
39
|
except Exception as e:
|
|
37
40
|
logger.error(e, exc_info=True)
|
|
41
|
+
|
|
42
|
+
def live_out(self, total: EmissionsData, delta: EmissionsData):
|
|
43
|
+
self.out(total, delta)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from codecarbon.external.logger import logger
|
|
2
|
+
from codecarbon.output_methods.base_output import BaseOutput
|
|
3
|
+
from codecarbon.output_methods.emissions_data import EmissionsData
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LogfireOutput(BaseOutput):
|
|
7
|
+
"""
|
|
8
|
+
Send emissions data to logfire
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
try:
|
|
13
|
+
from logfire import configure, metric_counter, metric_gauge
|
|
14
|
+
|
|
15
|
+
configure()
|
|
16
|
+
except ImportError:
|
|
17
|
+
logger.error(
|
|
18
|
+
"Logfire is not installed. Please install it using `pip install logfire`"
|
|
19
|
+
)
|
|
20
|
+
raise
|
|
21
|
+
|
|
22
|
+
# Counters
|
|
23
|
+
self.duration = metric_counter(
|
|
24
|
+
"codecarbon_duration", unit="(s)", description="Duration from last measure"
|
|
25
|
+
)
|
|
26
|
+
self.emissions = metric_counter(
|
|
27
|
+
"codecarbon_emissions",
|
|
28
|
+
unit="(kg)",
|
|
29
|
+
description="Emissions as CO₂-equivalents CO₂eq",
|
|
30
|
+
)
|
|
31
|
+
self.energy_consumed = metric_counter(
|
|
32
|
+
"codecarbon_energy_consumed",
|
|
33
|
+
unit="(kW)",
|
|
34
|
+
description="Sum of cpu_energy, gpu_energy and ram_energy",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Gauges
|
|
38
|
+
self.emissions_rate = metric_gauge(
|
|
39
|
+
"codecarbon_emissions_rate",
|
|
40
|
+
unit="(Kg/s)",
|
|
41
|
+
description="Emissions divided per duration",
|
|
42
|
+
)
|
|
43
|
+
self.cpu_power = metric_gauge(
|
|
44
|
+
"codecarbon_cpu_power", unit="(W)", description="CPU power"
|
|
45
|
+
)
|
|
46
|
+
self.gpu_power = metric_gauge(
|
|
47
|
+
"codecarbon_gpu_power", unit="(W)", description="GPU power"
|
|
48
|
+
)
|
|
49
|
+
self.ram_power = metric_gauge(
|
|
50
|
+
"codecarbon_ram_power", unit="(W)", description="RAM power"
|
|
51
|
+
)
|
|
52
|
+
self.cpu_energy = metric_gauge(
|
|
53
|
+
"codecarbon_cpu_energy", unit="(kWh)", description="Energy used per CPU"
|
|
54
|
+
)
|
|
55
|
+
self.gpu_energy = metric_gauge(
|
|
56
|
+
"codecarbon_gpu_energy", unit="(kWh)", description="Energy used per GPU"
|
|
57
|
+
)
|
|
58
|
+
self.ram_energy = metric_gauge(
|
|
59
|
+
"codecarbon_ram_energy", unit="(kWh)", description="Energy used per RAM"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def out(self, total: EmissionsData, delta: EmissionsData):
|
|
63
|
+
try:
|
|
64
|
+
self.duration.add(delta.duration)
|
|
65
|
+
self.emissions.add(delta.emissions)
|
|
66
|
+
self.emissions_rate.set(delta.emissions_rate)
|
|
67
|
+
self.cpu_power.set(delta.cpu_power)
|
|
68
|
+
self.gpu_power.set(delta.gpu_power)
|
|
69
|
+
self.ram_power.set(delta.ram_power)
|
|
70
|
+
self.cpu_energy.set(delta.cpu_energy)
|
|
71
|
+
self.gpu_energy.set(delta.gpu_energy)
|
|
72
|
+
self.ram_energy.set(delta.ram_energy)
|
|
73
|
+
self.energy_consumed.add(delta.energy_consumed)
|
|
74
|
+
logger.debug("Data sent to logfire")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.error(e, exc_info=True)
|
|
77
|
+
|
|
78
|
+
def live_out(self, total: EmissionsData, delta: EmissionsData):
|
|
79
|
+
self.out(total, delta)
|
|
@@ -1,26 +1,76 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
-
from prometheus_client import push_to_gateway
|
|
4
|
+
from prometheus_client import CollectorRegistry, Gauge, push_to_gateway
|
|
5
5
|
from prometheus_client.exposition import basic_auth_handler
|
|
6
6
|
|
|
7
7
|
from codecarbon.external.logger import logger
|
|
8
8
|
from codecarbon.output_methods.base_output import BaseOutput
|
|
9
9
|
from codecarbon.output_methods.emissions_data import EmissionsData
|
|
10
|
-
from codecarbon.output_methods.metrics.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
from codecarbon.output_methods.metrics.metric_docs import (
|
|
11
|
+
MetricDocumentation,
|
|
12
|
+
cpu_energy_doc,
|
|
13
|
+
cpu_power_doc,
|
|
14
|
+
duration_doc,
|
|
15
|
+
emissions_doc,
|
|
16
|
+
emissions_rate_doc,
|
|
17
|
+
energy_consumed_doc,
|
|
18
|
+
gpu_energy_doc,
|
|
19
|
+
gpu_power_doc,
|
|
20
|
+
ram_energy_doc,
|
|
21
|
+
ram_power_doc,
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
+
registry = CollectorRegistry()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# TODO: add labelnames
|
|
28
|
+
# timestamp: str
|
|
29
|
+
# run_id: str
|
|
30
|
+
# python_version: str
|
|
31
|
+
# longitude: float
|
|
32
|
+
# latitude: float
|
|
33
|
+
# on_cloud: str = "N"
|
|
34
|
+
|
|
35
|
+
# TODO: Set up the possible labels
|
|
36
|
+
labelnames = [
|
|
37
|
+
"project_name",
|
|
38
|
+
"country_name",
|
|
39
|
+
"country_iso_code",
|
|
40
|
+
"region",
|
|
41
|
+
"cloud_provider",
|
|
42
|
+
"cloud_region",
|
|
43
|
+
"os",
|
|
44
|
+
"codecarbon_version",
|
|
45
|
+
"cpu_model",
|
|
46
|
+
"cpu_count",
|
|
47
|
+
"gpu_model",
|
|
48
|
+
"gpu_count",
|
|
49
|
+
"tracking_mode",
|
|
50
|
+
"ram_total_size",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def generate_gauge(metric_doc: MetricDocumentation):
|
|
55
|
+
return Gauge(
|
|
56
|
+
metric_doc.name,
|
|
57
|
+
metric_doc.description,
|
|
58
|
+
labelnames,
|
|
59
|
+
registry=registry,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
duration_gauge = generate_gauge(duration_doc)
|
|
64
|
+
emissions_gauge = generate_gauge(emissions_doc)
|
|
65
|
+
emissions_rate_gauge = generate_gauge(emissions_rate_doc)
|
|
66
|
+
cpu_power_gauge = generate_gauge(cpu_power_doc)
|
|
67
|
+
gpu_power_gauge = generate_gauge(gpu_power_doc)
|
|
68
|
+
ram_power_gauge = generate_gauge(ram_power_doc)
|
|
69
|
+
cpu_energy_gauge = generate_gauge(cpu_energy_doc)
|
|
70
|
+
gpu_energy_gauge = generate_gauge(gpu_energy_doc)
|
|
71
|
+
ram_energy_gauge = generate_gauge(ram_energy_doc)
|
|
72
|
+
energy_consumed_gauge = generate_gauge(energy_consumed_doc)
|
|
73
|
+
|
|
24
74
|
|
|
25
75
|
class PrometheusOutput(BaseOutput):
|
|
26
76
|
"""
|
|
@@ -30,12 +80,15 @@ class PrometheusOutput(BaseOutput):
|
|
|
30
80
|
def __init__(self, prometheus_url: str):
|
|
31
81
|
self.prometheus_url = prometheus_url
|
|
32
82
|
|
|
33
|
-
def out(self,
|
|
83
|
+
def out(self, total: EmissionsData, delta: EmissionsData):
|
|
34
84
|
try:
|
|
35
|
-
self.add_emission(dataclasses.asdict(
|
|
85
|
+
self.add_emission(dataclasses.asdict(delta))
|
|
36
86
|
except Exception as e:
|
|
37
87
|
logger.error(e, exc_info=True)
|
|
38
88
|
|
|
89
|
+
def live_out(self, total: EmissionsData, delta: EmissionsData):
|
|
90
|
+
self.out(total, delta)
|
|
91
|
+
|
|
39
92
|
def _auth_handler(self, url, method, timeout, headers, data):
|
|
40
93
|
username = os.getenv("PROMETHEUS_USERNAME")
|
|
41
94
|
password = os.getenv("PROMETHEUS_PASSWORD")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.4.2"
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
provider,providerName,offsetRatio,region,regionName,country_name,countryIsoCode,state,city,impact,source,comment
|
|
2
|
-
gcp,Google Cloud Platform,100,asia-east1,,Taiwan,TWN,,Changhua County,456,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
3
|
-
gcp,Google Cloud Platform,100,asia-east2,,China,CHN,,Hong Kong,360,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
4
|
-
gcp,Google Cloud Platform,100,asia-northeast1,,Japan,JPN,,Tokyo,464,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
5
|
-
gcp,Google Cloud Platform,100,asia-northeast2,,Japan,JPN,,Osaka,384,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
6
|
-
gcp,Google Cloud Platform,100,asia-northeast3,,Korea,KOR,South Korea,Seoul,425,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
7
|
-
gcp,Google Cloud Platform,100,asia-south1,,India,IND,,Mumbai,633,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
8
|
-
gcp,Google Cloud Platform,100,asia-south2,,India,IND,,Delhi,633,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
9
|
-
gcp,Google Cloud Platform,100,asia-southeast1,,Singapore,SGP,,Jurong West,372,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
10
|
-
gcp,Google Cloud Platform,100,asia-southeast2,,Indonesia,IDN,,Jakarta,580,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
11
|
-
gcp,Google Cloud Platform,100,australia-southeast1,,Australia,AUS,,Sydney,723,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
12
|
-
gcp,Google Cloud Platform,100,australia-southeast2,,Australia,AUS,,Melbourne,746,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
13
|
-
gcp,Google Cloud Platform,100,europe-central2,,Poland,POL,,Warsaw,576,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
14
|
-
gcp,Google Cloud Platform,100,europe-north1,,Finland,FIN,,Hamina,127,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
15
|
-
gcp,Google Cloud Platform,100,europe-southwest1,,Spain,ESP,,Madrid,121,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
16
|
-
gcp,Google Cloud Platform,100,europe-west1,,Belgium,BEL,,St. Ghislain,110,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
17
|
-
gcp,Google Cloud Platform,100,europe-west2,,United Kingdom,GBR,England,London,172,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
18
|
-
gcp,Google Cloud Platform,100,europe-west3,,Germany,DEU,,Frankfurt,269,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
19
|
-
gcp,Google Cloud Platform,100,europe-west4,,Netherlands,NLD,,Eemshaven,282,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
20
|
-
gcp,Google Cloud Platform,100,europe-west6,,Switzerland,CHE,,Zurich,86,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
21
|
-
gcp,Google Cloud Platform,100,europe-west8,,Italy,ITA,,Milan,298,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
22
|
-
gcp,Google Cloud Platform,100,europe-west9,,France,FRA,,Paris,59,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
23
|
-
gcp,Google Cloud Platform,100,northamerica-northeast1,,Canada,CAN,Quebec,Montreal,142,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
24
|
-
gcp,Google Cloud Platform,100,northamerica-northeast2,,Canada,CAN,,Toronto,142,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
25
|
-
gcp,Google Cloud Platform,100,southamerica-east1,,Brazil,BRA,,Sao Paulo,129,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
26
|
-
gcp,Google Cloud Platform,100,southamerica-west1,,Chili,CHL,,Santiago,190,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
27
|
-
gcp,Google Cloud Platform,100,us-central1,,USA,USA,Iowa,Council Bluffs,394,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
28
|
-
gcp,Google Cloud Platform,100,us-east1,,USA,USA,South Carolina,Moncks Corner,434,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
29
|
-
gcp,Google Cloud Platform,100,us-east4,,USA,USA,Northern Virginia,Ashburn,309,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
30
|
-
gcp,Google Cloud Platform,100,us-east5,,USA,USA,,Columbus,309,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
31
|
-
gcp,Google Cloud Platform,100,us-south1,,USA,USA,,Dallas,296,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
32
|
-
gcp,Google Cloud Platform,100,us-west1,,USA,USA,Oregon,The Dalles,60,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
33
|
-
gcp,Google Cloud Platform,100,us-west2,,USA,USA,California,Los Angeles,190,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
34
|
-
gcp,Google Cloud Platform,100,us-west3,,USA,USA,Utah,Salt Lake City,448,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
35
|
-
gcp,Google Cloud Platform,100,us-west4,,USA,USA,Nevada,Las Vegas,365,https://cloud.google.com/sustainability/region-carbon?hl=fr,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
|
|
3
|
-
from codecarbon.output_methods.emissions_data import EmissionsData
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class BaseOutput(ABC):
|
|
7
|
-
"""
|
|
8
|
-
An abstract class that requires children to inherit a single method,
|
|
9
|
-
`out` which is used for persisting data. This could be by saving it to a file,
|
|
10
|
-
posting to Json Box, saving to a database, sending a slack message etc.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
@abstractmethod
|
|
14
|
-
def out(self, data: EmissionsData):
|
|
15
|
-
pass
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
from prometheus_client import CollectorRegistry, Gauge
|
|
2
|
-
|
|
3
|
-
from codecarbon.output_methods.metrics.metric_docs import (
|
|
4
|
-
MetricDocumentation,
|
|
5
|
-
cpu_energy_doc,
|
|
6
|
-
cpu_power_doc,
|
|
7
|
-
duration_doc,
|
|
8
|
-
emissions_doc,
|
|
9
|
-
emissions_rate_doc,
|
|
10
|
-
energy_consumed_doc,
|
|
11
|
-
gpu_energy_doc,
|
|
12
|
-
gpu_power_doc,
|
|
13
|
-
ram_energy_doc,
|
|
14
|
-
ram_power_doc,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
registry = CollectorRegistry()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# TODO: add labelnames
|
|
21
|
-
# timestamp: str
|
|
22
|
-
# run_id: str
|
|
23
|
-
# python_version: str
|
|
24
|
-
# longitude: float
|
|
25
|
-
# latitude: float
|
|
26
|
-
# on_cloud: str = "N"
|
|
27
|
-
|
|
28
|
-
# TODO: Set up the possible labels
|
|
29
|
-
labelnames = [
|
|
30
|
-
"project_name",
|
|
31
|
-
"country_name",
|
|
32
|
-
"country_iso_code",
|
|
33
|
-
"region",
|
|
34
|
-
"cloud_provider",
|
|
35
|
-
"cloud_region",
|
|
36
|
-
"os",
|
|
37
|
-
"codecarbon_version",
|
|
38
|
-
"cpu_model",
|
|
39
|
-
"cpu_count",
|
|
40
|
-
"gpu_model",
|
|
41
|
-
"gpu_count",
|
|
42
|
-
"tracking_mode",
|
|
43
|
-
"ram_total_size",
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def generate_gauge(metric_doc: MetricDocumentation):
|
|
48
|
-
return Gauge(
|
|
49
|
-
metric_doc.name,
|
|
50
|
-
metric_doc.description,
|
|
51
|
-
labelnames,
|
|
52
|
-
registry=registry,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
duration_gauge = generate_gauge(duration_doc)
|
|
57
|
-
emissions_gauge = generate_gauge(emissions_doc)
|
|
58
|
-
emissions_rate_gauge = generate_gauge(emissions_rate_doc)
|
|
59
|
-
cpu_power_gauge = generate_gauge(cpu_power_doc)
|
|
60
|
-
gpu_power_gauge = generate_gauge(gpu_power_doc)
|
|
61
|
-
ram_power_gauge = generate_gauge(ram_power_doc)
|
|
62
|
-
cpu_energy_gauge = generate_gauge(cpu_energy_doc)
|
|
63
|
-
gpu_energy_gauge = generate_gauge(gpu_energy_doc)
|
|
64
|
-
ram_energy_gauge = generate_gauge(ram_energy_doc)
|
|
65
|
-
energy_consumed_gauge = generate_gauge(energy_consumed_doc)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2016/canada_energy_mix.json
RENAMED
|
File without changes
|
{codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2016/global_energy_mix-old.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/03_add_eu_data.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/carbon_intensity_per_source.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|