Skip to content

Commit 9257fdc

Browse files
committed
code: add logging to file
1 parent 8e49c14 commit 9257fdc

File tree

4 files changed

+78
-8
lines changed

4 files changed

+78
-8
lines changed

Snakefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
#
33
# SPDX-License-Identifier: GPL-3.0-only
44

5+
import pathlib
56

67
configfile: "config.yaml"
78

8-
99
rule compile_cost_assumptions:
1010
input:
1111
inflation_rate="inputs/prc_hicp_aind__custom_9928419_spreadsheet.xlsx",
@@ -32,6 +32,8 @@ rule compile_cost_assumptions:
3232
mem=500,
3333
conda:
3434
"environment.yaml"
35+
log:
36+
pathlib.Path("logs", "compile_cost_assumptions.log"),
3537
script:
3638
"scripts/compile_cost_assumptions.py"
3739

@@ -54,6 +56,8 @@ rule compile_cost_assumptions_usa:
5456
mem=500,
5557
conda:
5658
"environment.yaml"
59+
log:
60+
pathlib.Path("logs", "compile_cost_assumptions_usa.log"),
5761
script:
5862
"scripts/compile_cost_assumptions_usa.py"
5963

scripts/_helpers.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import numpy as np
1111
import pandas as pd
1212

13+
import logging
14+
import sys
15+
1316

1417
class Dict(dict):
1518
"""
@@ -251,3 +254,55 @@ def get_factor(inflation_rate_df, ref_year, eur_year_val):
251254
)
252255

253256
return costs
257+
258+
259+
def configure_logging(snakemake, skip_handlers=False):
260+
"""
261+
Configure the basic behaviour for the logging module.
262+
263+
Note: Must only be called once from the __main__ section of a script.
264+
265+
The setup includes printing log messages to STDERR and to a log file defined
266+
by either (in priority order): snakemake.log.python, snakemake.log[0] or "logs/{rulename}.log".
267+
Additional keywords from logging.basicConfig are accepted via the snakemake configuration
268+
file under snakemake.config.logging.
269+
270+
Parameters
271+
----------
272+
snakemake : snakemake object
273+
Your snakemake object containing a snakemake.config and snakemake.log.
274+
skip_handlers : True | False (default)
275+
Do (not) skip the default handlers created for redirecting output to STDERR and file.
276+
"""
277+
278+
kwargs = snakemake.config.get("logging", dict()).copy()
279+
kwargs.setdefault("level", "INFO")
280+
281+
if skip_handlers is False:
282+
fallback_path = Path(__file__).parent.joinpath(
283+
"..", "logs", f"{snakemake.rule}.log"
284+
)
285+
logfile = snakemake.log.get(
286+
"python", snakemake.log[0] if snakemake.log else fallback_path
287+
)
288+
kwargs.update(
289+
{
290+
"handlers": [
291+
# Prefer the 'python' log, otherwise take the first log for each
292+
# Snakemake rule
293+
logging.FileHandler(logfile),
294+
logging.StreamHandler(),
295+
]
296+
}
297+
)
298+
logging.basicConfig(**kwargs)
299+
300+
# Setup a function to handle uncaught exceptions and include them with their stacktrace into logfiles
301+
def handle_exception(exc_type, exc_value, exc_traceback):
302+
# Log the exception
303+
logger = logging.getLogger()
304+
logger.error(
305+
"Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
306+
)
307+
308+
sys.excepthook = handle_exception

scripts/compile_cost_assumptions.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,10 @@
3333

3434
import numpy as np
3535
import pandas as pd
36-
from _helpers import adjust_for_inflation
36+
from _helpers import adjust_for_inflation, configure_logging, mock_snakemake
3737
from currency_converter import ECB_URL, CurrencyConverter
3838
from scipy import interpolate
3939

40-
logging.basicConfig(level=logging.INFO)
4140
logger = logging.getLogger(__name__)
4241

4342
try:
@@ -3943,11 +3942,10 @@ def prepare_inflation_rate(fn: str) -> pd.DataFrame:
39433942
# ---------- MAIN ------------------------------------------------------------
39443943
if __name__ == "__main__":
39453944
if "snakemake" not in globals():
3946-
from _helpers import mock_snakemake
3947-
3948-
# os.chdir(os.path.join(os.getcwd(), "scripts"))
39493945
snakemake = mock_snakemake("compile_cost_assumptions")
39503946

3947+
configure_logging(snakemake)
3948+
39513949
years_list = list(snakemake.config["years"])
39523950
inflation_rate = prepare_inflation_rate(snakemake.input.inflation_rate)
39533951

scripts/compile_cost_assumptions_usa.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
"""
1111

1212
import pathlib
13-
13+
import logging
1414
import numpy as np
1515
import pandas as pd
16-
from _helpers import adjust_for_inflation, mock_snakemake
16+
from _helpers import adjust_for_inflation, configure_logging, mock_snakemake
1717
from compile_cost_assumptions import prepare_inflation_rate
1818

19+
logger = logging.getLogger(__name__)
20+
1921

2022
def get_conversion_dictionary(flag: str) -> dict:
2123
"""
@@ -904,6 +906,8 @@ def duplicate_fuel_cost(input_file_path: str, list_of_years: list) -> pd.DataFra
904906
if "snakemake" not in globals():
905907
snakemake = mock_snakemake("compile_cost_assumptions_usa")
906908

909+
configure_logging(snakemake)
910+
907911
year_list = sorted(snakemake.config["years"])
908912
num_digits = snakemake.config["ndigits"]
909913
eur_reference_year = snakemake.config["eur_year"]
@@ -922,6 +926,8 @@ def duplicate_fuel_cost(input_file_path: str, list_of_years: list) -> pd.DataFra
922926
"nrel_atb_technology_to_remove"
923927
]
924928

929+
logger.info("Configuration variables are set")
930+
925931
if len(set(snakemake.config["years"])) < len(snakemake.config["years"]):
926932
raise Exception(
927933
"Please verify the list of cost files. It may contain duplicates."
@@ -935,9 +941,13 @@ def duplicate_fuel_cost(input_file_path: str, list_of_years: list) -> pd.DataFra
935941
# get the discount rate values for the US
936942
discount_rate_df = pd.read_csv(input_file_discount_rate)
937943

944+
logger.info("discount_rate file for the US has been read in")
945+
938946
# get the fuel costs values for the US
939947
fuel_costs_df = duplicate_fuel_cost(input_file_fuel_costs, year_list)
940948

949+
logger.info("fuel_cost file for the US has been read in")
950+
941951
for year_val in year_list:
942952
# get the cost file to modify
943953
input_cost_path = [
@@ -956,6 +966,8 @@ def duplicate_fuel_cost(input_file_path: str, list_of_years: list) -> pd.DataFra
956966
else:
957967
raise Exception(f"{year_val} is not a considered year")
958968

969+
logger.info("The file {} is used for year {}".format(input_atb_path, year_val))
970+
959971
manual_input_usa_df = pre_process_manual_input_usa(
960972
input_file_manual_input_usa,
961973
input_file_eur_inflation_rate,
@@ -1060,6 +1072,7 @@ def duplicate_fuel_cost(input_file_path: str, list_of_years: list) -> pd.DataFra
10601072
if len(output_cost_path_list) == 1:
10611073
output_cost_path = output_cost_path_list[0]
10621074
updated_cost_df.to_csv(output_cost_path, index=False)
1075+
logger.info("The cost assumptions file for the US has been compiled for year {}".format(year_val))
10631076
else:
10641077
raise Exception(
10651078
"Please verify the list of cost files. It may contain duplicates."

0 commit comments

Comments
 (0)