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.
Files changed (72) hide show
  1. {codecarbon-2.4.2 → codecarbon-2.5.0}/.gitignore +1 -0
  2. {codecarbon-2.4.2 → codecarbon-2.5.0}/PKG-INFO +1 -1
  3. codecarbon-2.5.0/codecarbon/_version.py +1 -0
  4. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/api_client.py +8 -0
  5. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/powermetrics.py +8 -0
  6. codecarbon-2.5.0/codecarbon/data/cloud/impact.csv +37 -0
  7. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/emissions_tracker.py +99 -72
  8. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/geography.py +8 -4
  9. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/hardware.py +3 -0
  10. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output.py +2 -3
  11. codecarbon-2.5.0/codecarbon/output_methods/base_output.py +24 -0
  12. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/file.py +22 -15
  13. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/http.py +4 -4
  14. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/logger.py +10 -4
  15. codecarbon-2.5.0/codecarbon/output_methods/metrics/logfire.py +79 -0
  16. {codecarbon-2.4.2/codecarbon/output_methods/metrics/prometheus → codecarbon-2.5.0/codecarbon/output_methods/metrics}/prometheus.py +68 -15
  17. codecarbon-2.4.2/codecarbon/_version.py +0 -1
  18. codecarbon-2.4.2/codecarbon/data/cloud/impact.csv +0 -35
  19. codecarbon-2.4.2/codecarbon/output_methods/base_output.py +0 -15
  20. codecarbon-2.4.2/codecarbon/output_methods/metrics/prometheus/metrics.py +0 -65
  21. codecarbon-2.4.2/codecarbon/viz/assets/__init__.py +0 -0
  22. {codecarbon-2.4.2 → codecarbon-2.5.0}/LICENSE +0 -0
  23. {codecarbon-2.4.2 → codecarbon-2.5.0}/README.md +0 -0
  24. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/__init__.py +0 -0
  25. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/cli/__init__.py +0 -0
  26. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/cli/cli_utils.py +0 -0
  27. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/cli/main.py +0 -0
  28. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/__init__.py +0 -0
  29. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/cloud.py +0 -0
  30. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/co2_signal.py +0 -0
  31. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/config.py +0 -0
  32. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/cpu.py +0 -0
  33. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/emissions.py +0 -0
  34. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/gpu.py +0 -0
  35. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/measure.py +0 -0
  36. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/rapl.py +0 -0
  37. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/schemas.py +0 -0
  38. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/units.py +0 -0
  39. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/core/util.py +0 -0
  40. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/canada_provinces.geojson +0 -0
  41. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/hardware/cpu_power.csv +0 -0
  42. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2016/canada_energy_mix.json +0 -0
  43. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2016/global_energy_mix-old.json +0 -0
  44. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2016/usa_emissions.json +0 -0
  45. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/01_get_world_carbon_intensity.ipynb +0 -0
  46. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/02_convert_csv_to_json.ipynb +0 -0
  47. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/03_add_eu_data.ipynb +0 -0
  48. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2020/eu-carbon-intensity-electricity.csv +0 -0
  49. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/2023-07-07-22-40-48.png +0 -0
  50. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/carbon_intensity_per_source.json +0 -0
  51. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/global_energy_mix.json +0 -0
  52. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/our_world_in_data-2022_data.ipynb +0 -0
  53. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/data/private_infra/world_energy_mix.csv +0 -0
  54. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/__init__.py +0 -0
  55. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/logger.py +0 -0
  56. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/scheduler.py +0 -0
  57. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/external/task.py +0 -0
  58. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/input.py +0 -0
  59. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/__init__.py +0 -0
  60. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/emissions_data.py +0 -0
  61. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/metrics/__init__.py +0 -0
  62. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/output_methods/metrics/metric_docs.py +0 -0
  63. {codecarbon-2.4.2/codecarbon/output_methods/metrics/prometheus → codecarbon-2.5.0/codecarbon/viz}/__init__.py +0 -0
  64. {codecarbon-2.4.2/codecarbon/viz → codecarbon-2.5.0/codecarbon/viz/assets}/__init__.py +0 -0
  65. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/assets/car_icon.png +0 -0
  66. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/assets/house_icon.png +0 -0
  67. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/assets/tv_icon.png +0 -0
  68. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/carbonboard.py +0 -0
  69. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/carbonboard_on_api.py +0 -0
  70. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/components.py +0 -0
  71. {codecarbon-2.4.2 → codecarbon-2.5.0}/codecarbon/viz/data.py +0 -0
  72. {codecarbon-2.4.2 → codecarbon-2.5.0}/pyproject.toml +0 -0
@@ -110,6 +110,7 @@ venv.bak/
110
110
 
111
111
  # idea
112
112
  .idea/
113
+ *.iml
113
114
 
114
115
  # Sphinx
115
116
  docs/_build/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: codecarbon
3
- Version: 2.4.2
3
+ Version: 2.5.0
4
4
  Project-URL: Homepage, https://codecarbon.io/
5
5
  Project-URL: Repository, https://github.com/mlco2/codecarbon
6
6
  Project-URL: Dashboard, http://dashboard.codecarbon.io/
@@ -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
- # If _gpu_ids is a string or a list of int, parse it to a list of ints
275
- if isinstance(self._gpu_ids, str) or (
276
- isinstance(self._gpu_ids, list)
277
- and all(isinstance(gpu_id, int) for gpu_id in self._gpu_ids)
278
- ):
279
- self._gpu_ids: List[int] = parse_gpu_ids(self._gpu_ids)
280
- self._conf["gpu_ids"] = self._gpu_ids
281
- self._conf["gpu_count"] = len(self._gpu_ids)
282
- else:
283
- logger.warning(
284
- "Invalid gpu_ids format. Expected a string or a list of ints."
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.persistence_objs.append(
408
+ self._output_handlers.append(
406
409
  FileOutput(
407
- os.path.join(self._output_dir, self._output_file),
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.persistence_objs.append(self._logging_logger)
417
+ self._output_handlers.append(self._logging_logger)
414
418
 
415
419
  if self._emissions_endpoint:
416
- self.persistence_objs.append(HTTPOutput(self._emissions_endpoint))
420
+ self._output_handlers.append(HTTPOutput(self._emissions_endpoint))
417
421
 
418
422
  if self._save_to_api:
419
- self._cc_api__out = CodeCarbonAPIOutput(
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 = self._cc_api__out.run_id
426
- self.persistence_objs.append(self._cc_api__out)
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._cc_prometheus_out = PrometheusOutput(self._prometheus_url)
433
- self.persistence_objs.append(self._cc_prometheus_out)
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(delta=True)
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(delta=True)
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 = emissions_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._persist_data(emissions_data)
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(emissions_data, experiment_name=self._experiment_name)
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(self, emissions_data, experiment_name=None):
564
- for persistence in self.persistence_objs:
565
- if isinstance(persistence, CodeCarbonAPIOutput):
566
- emissions_data = self._prepare_emissions_data(delta=True)
567
-
568
- persistence.out(emissions_data)
569
- if isinstance(persistence, FileOutput):
570
- if len(self._tasks) > 0:
571
- task_emissions_data = []
572
- for task in self._tasks:
573
- task_emissions_data.append(self._tasks[task].out())
574
- persistence.task_out(
575
- task_emissions_data, experiment_name, self._output_dir
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, delta=False) -> EmissionsData:
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._cc_api__out is not None or self._cc_prometheus_out is not None
745
- ) and self._api_call_interval != -1:
746
- if self._measure_occurrence >= self._api_call_interval:
747
- emissions = self._prepare_emissions_data(delta=True)
748
- logger.info(
749
- f"{emissions.emissions_rate * 1000:.6f} g.CO2eq/s mean an estimation of "
750
- + f"{emissions.emissions_rate * 3600 * 24 * 365:,} kg.CO2eq/year"
751
- )
752
- if self._cc_api__out:
753
- self._cc_api__out.out(emissions)
754
- if self._cc_prometheus_out:
755
- self._cc_prometheus_out.out(emissions)
756
- self._measure_occurrence = 0
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"]["region"],
36
- "azure": lambda x: x["metadata"]["compute"]["location"],
37
- "gcp": lambda x: extract_gcp_region(x["metadata"]["zone"]),
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.prometheus import ( # noqa: F401
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__(self, save_file_path: str, on_csv_write: str = "append"):
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: str = 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, data: EmissionsData):
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(data):
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=data.values.keys())
43
- df = pd.concat([df, pd.DataFrame.from_records([dict(data.values)])])
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(data.values)])])
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 == data.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(data.values)])])
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 ({data.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(data.values)])])
65
+ df = pd.concat([df, pd.DataFrame.from_records([dict(total.values)])])
59
66
  else:
60
- df.at[df.run_id == data.run_id, data.values.keys()] = (
61
- data.values.values()
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, output_dir):
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, data: EmissionsData):
22
+ def out(self, total: EmissionsData, delta: EmissionsData):
23
23
  try:
24
- payload = dataclasses.asdict(data)
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, data: EmissionsData):
53
+ def out(self, total: EmissionsData, delta: EmissionsData):
54
54
  try:
55
- self.api.add_emission(dataclasses.asdict(data))
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, data: EmissionsData):
19
+ def out(self, total: EmissionsData, delta: EmissionsData):
20
20
  try:
21
- payload = dataclasses.asdict(data)
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, data: EmissionsData):
35
+ def out(self, total: EmissionsData, delta: EmissionsData):
33
36
  try:
34
- payload = dataclasses.asdict(data)
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.prometheus.metrics import (
11
- cpu_energy_gauge,
12
- cpu_power_gauge,
13
- duration_gauge,
14
- emissions_gauge,
15
- emissions_rate_gauge,
16
- energy_consumed_gauge,
17
- gpu_energy_gauge,
18
- gpu_power_gauge,
19
- ram_energy_gauge,
20
- ram_power_gauge,
21
- registry,
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, data: EmissionsData):
83
+ def out(self, total: EmissionsData, delta: EmissionsData):
34
84
  try:
35
- self.add_emission(dataclasses.asdict(data))
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