From 6c2d8898ab2783abbd17c0cb26aec36a6de9be3e Mon Sep 17 00:00:00 2001
From: Daniel Vedder <daniel.vedder@idiv.de>
Date: Wed, 5 Jun 2024 15:34:13 +0200
Subject: [PATCH] Log output can now be routed to screen, to file, or to both

---
 src/core/output.jl        | 19 +++++++++++++------
 src/core/simulation.jl    |  7 +++++--
 src/parameters.toml       |  7 ++++---
 test/io_tests.jl          |  9 ++++++++-
 test/test_parameters.toml |  1 +
 5 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/src/core/output.jl b/src/core/output.jl
index 6d1fc2d..9a3eefe 100644
--- a/src/core/output.jl
+++ b/src/core/output.jl
@@ -30,23 +30,30 @@ function createdatadir(outdir::String, overwrite::Union{Bool,String})
 end
 
 """
-    modellogger(loglevel, outdir)
+    modellogger(loglevel, outdir, output="both")
 
 Create a logger object that writes output both to screen and to a logfile.
 This object is stored as `model.logger` and can then be used with `with_logger()`.
 Note: requires [`createdatadir`](@ref) to be run first.
 """
-function modellogger(loglevel::String, outdir::String)
+function modellogger(loglevel::String, outdir::String, output::String="both")
     !isdir(outdir) && #TODO replace with exception
         Base.error("$(outdir) does not exist. Call `createdatadir()` before `modellogger()`.")
     loglevel == "debug" ? loglevel = Logging.Debug :
         loglevel == "warn" ? loglevel = Logging.Warn :
         loglevel == "info" ? loglevel = Logging.Info :
         Base.error("Invalid loglevel $loglevel, should be debug/info/warn.") #TODO make exception
-    #XXX If this is a parallel run, should we turn off logging to screen?
-    logfile = open(joinpath(outdir, LOGFILE), "w+")
-    TeeLogger(ConsoleLogger(logfile, loglevel),
-              ConsoleLogger(stdout, loglevel))
+    (output in ["file", "both"]) && (logfile = open(joinpath(outdir, LOGFILE), "w+"))
+    if output == "both"
+        return TeeLogger(ConsoleLogger(logfile, loglevel),
+                         ConsoleLogger(stdout, loglevel))
+    elseif output == "file"
+        return ConsoleLogger(logfile, loglevel)
+    elseif output == "screen"
+        return ConsoleLogger(stdout, loglevel)
+    else
+        Base.error("Invalid log output target $output, should be file/screen/both.")
+    end
 end
 
 """
diff --git a/src/core/simulation.jl b/src/core/simulation.jl
index dd454c7..646f47a 100644
--- a/src/core/simulation.jl
+++ b/src/core/simulation.jl
@@ -104,8 +104,11 @@ a helper function for `initialise()`.
 function initmodel(settings::Dict{String, Any})
     #TODO catch exceptions and print them to the log file
     @debug "Initialising model object."
-    createdatadir(settings["core.outdir"], settings["core.overwrite"])
-    logger = modellogger(settings["core.loglevel"], settings["core.outdir"])
+    createdatadir(settings["core.outdir"],
+                  settings["core.overwrite"])
+    logger = modellogger(settings["core.loglevel"],
+                         settings["core.outdir"],
+                         settings["core.logoutput"])
     with_logger(logger) do
         landscape = initlandscape(settings["world.landcovermap"],
                                   settings["world.farmfieldsmap"])
diff --git a/src/parameters.toml b/src/parameters.toml
index 3a068c7..272bb25 100644
--- a/src/parameters.toml
+++ b/src/parameters.toml
@@ -10,6 +10,7 @@
 configfile = "src/parameters.toml" # location of the configuration file
 outdir = "results" # location and name of the output folder
 overwrite = "ask" # overwrite the output directory? (true/false/"ask")
+logoutput = "both" # log output to screen/file/both
 csvoutput = true # save collected data in CSV files
 visualise = true # generate result graphs
 storedata = true # keep collected data in memory
@@ -17,9 +18,9 @@ figureformat = "pdf" # file format to use for graphical output
 loglevel = "debug" # verbosity level: "debug", "info", "warn"
 processors = 2 # number of processors to use on parallel runs
 seed = 2 # seed value for the RNG (0 -> random value)
-# dates to start and end the simulation
-startdate = 2022-01-01
-enddate = 2022-12-31
+startdate = 2022-01-01 # first day of the simulation
+#enddate = 2022-12-31 # last day of the simulation
+enddate = 2022-03-31 # last day of the simulation (test value)
 
 [world]
 landcovermap = "data/regions/jena-small/landcover.tif" # location of the landcover map
diff --git a/test/io_tests.jl b/test/io_tests.jl
index 214e549..71c8f0c 100644
--- a/test/io_tests.jl
+++ b/test/io_tests.jl
@@ -19,13 +19,20 @@ end
     outdir = @param(core.outdir)
     Ps.createdatadir(outdir, @param(core.overwrite))
     @test isdir(outdir)
-    logger = Ps.modellogger(@param(core.loglevel), outdir)
     @test_logs((:debug, "Setting up output directory results_testsuite."),
                min_level=Logging.Debug, match_mode=:any,
                Ps.saveinputfiles(model))
     @test isfile(joinpath(outdir, @param(world.landcovermap)))
     @test isfile(joinpath(outdir, @param(world.farmfieldsmap)))
     @test isfile(joinpath(outdir, @param(core.configfile)))
+    # test log output to screen/file/both
+    #XXX cannot test logger output due to https://github.com/JuliaLang/julia/issues/48456
+    logger1 = Ps.modellogger(@param(core.loglevel), outdir, "screen")
+    @test !isfile(joinpath(outdir, Ps.LOGFILE))
+    logger2 = Ps.modellogger(@param(core.loglevel), outdir, "file")
+    @test isfile(joinpath(outdir, Ps.LOGFILE))
+    rm(joinpath(outdir, Ps.LOGFILE))
+    logger3 = Ps.modellogger(@param(core.loglevel), outdir, "both")
     @test isfile(joinpath(outdir, Ps.LOGFILE))
     # check whether the overwrite warning/protection works
     @test_logs((:warn, "Overwriting existing output directory $(outdir)."),
diff --git a/test/test_parameters.toml b/test/test_parameters.toml
index f34a20d..3a24704 100644
--- a/test/test_parameters.toml
+++ b/test/test_parameters.toml
@@ -8,6 +8,7 @@
 [core]
 configfile = "test_parameters.toml" # location of the configuration file
 outdir = "results_testsuite" # location and name of the output folder
+logoutput = "both"
 overwrite = true # overwrite the output directory? (true/false/"ask")
 csvoutput = true # save collected data in CSV files
 visualise = true # generate result graphs
-- 
GitLab