7

I have started using Mathematica to automate file backup operations. The logistics I need make Mathematica a good choice. I am testing and developing the Notebook manually, and Print outputs document the operational events. The saved Notebook provides a record of the process for that day.

I would like to have an easy way to direct Print output to a separate file, for example, by adding

DirectPrintOutputTo["filename "<>DateList["Year","Month","Day"]<>".txt"] 

at the beginning of a Notebook. The advantages of this approach seem obvious. I assume I would need to load my function from a package before calling it. Is something like this this possible? Any suggestions about how to do it?

Alternatively, maybe I could write a print function called PrintTo that would, on first use, open an output file with the name assigned to printToFileName, and I could use the editor to change all the Print functions to PrintTo functions. Not so fancy, but I think I know how to how to do it.

I think that I answered my own question, in the sense that my alternative idea seems straightforward and pretty implementable.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
George Wolfe
  • 5,462
  • 21
  • 43
  • My current scripting approach used Print statements to print results to the scripting notebook. I don't think PutAppend would work (but I may not understand your suggestion). – George Wolfe Jul 26 '13 at 19:34
  • I have missunderstood the question, I thought you are going to write this not to change Print. So it is not good suggestion then :) – Kuba Jul 26 '13 at 19:54
  • 2
    Possible duplicates: (5040), (7081), (22584) – Mr.Wizard Jul 26 '13 at 20:31
  • I wonder if one could use 'TagSet' to make extend the functionality of 'Print' to your specific needs. I don't have much time to think through this now (heading away for a week). May I can work something up later tonight. – Jagra Jul 27 '13 at 17:30

3 Answers3

7

From the documentation for Print: "Print sends its output to the channel $Output."

Therefore you can just do this:

$Output = OpenWrite["filename " <> DateList["Year", "Month", "Day"] <> ".txt"];

before you run the file.

Something more structured would be like this:

stream = OpenWrite["c:/users/gre/desktop/derp.txt"];
AppendTo[$Output, stream];

(* run your file here *)

Close[stream];
$Output = Most[$Output];

Since Print by default outputs to stdout, you could also pipe the output by running the notebook as a script.

amr
  • 5,487
  • 1
  • 22
  • 32
  • I tried using Block[{$Output = ..., but this didn't seem to work for $Output. – amr Jul 26 '13 at 20:20
  • Beat me to the punch about $Output. However, I was working on a generic environment. – rcollyer Jul 26 '13 at 20:21
  • @rcollyer Well you both beat me, so... – Mr.Wizard Jul 26 '13 at 20:23
  • @rcollyer agreed. usually i go the lazy route :D – amr Jul 26 '13 at 20:23
  • @Mr.Wizard yea this is quite hilarious. lmao – amr Jul 26 '13 at 20:24
  • 3
    @Mr.Wizard I beat both of you a long time ago, and Leonid. :) – rcollyer Jul 26 '13 at 20:24
  • @Mr.Wizard oops. thanks – amr Jul 26 '13 at 20:25
  • Speaking of which, @Mr.Wizard, where's the upvote love?!? :D – rcollyer Jul 26 '13 at 20:26
  • @rcollyer The comments under the answer you linked to made me smile. As for votes, I've already upvoted your S.O. answer, and I've deleted mine here. I also now think this question is a duplicate. – Mr.Wizard Jul 26 '13 at 20:41
  • @amr, I was very pleased with this simple solution, however, things printed in a subkernel during for instance ParallelDo don't get redirected to the log. Any way to do this? (I asked it as a separate question https://mathematica.stackexchange.com/questions/174771/redirecting-all-prints-including-those-in-subkernel-to-file?noredirect=1#comment458817_174771). – Kvothe Jun 06 '18 at 16:06
2

I don't recommend modifying Print to redirect the output when it is not necessary. Instead, you can build up a custom solution that suits your needs. I use a variant of the following for logging in my projects and you should be able to tweak them to your application.

Clear[logFile, writeLog, showLog];
logFile := "project-" <> DateString[##, {"Year", "Month", "Day"}] <> ".log" &;

writeLog[message_String] := 
    Module[{str = OpenAppend[logFile[]], date = DateString[]}, 
        WriteString[str, StringJoin @@ Riffle[{date, message, "\n"}, "\t"]];
        Close[str];
    ]

showLog := FilePrint@logFile@## &;
clearLog := Module[{str = OpenWrite[logFile@##]}, Close@str;] &

Note the use of OpenAppend and OpenWrite in the above functions. OpenAppend creates a new file if none exists, but appends to an already existing file. OpenWrite, on the other hand, simply creates a new empty file regardless of whether a file already exists with that name (thus erasing data, which is what we want here).

Since writeLog just takes a string as input, you can simply replace your Print commands with writeLog and it should work fine.

logFile[]
(* "project-20130726.log" *)

writeLog["Hello world"];
showLog[]
(* Fri 26 Jul 2013 12:51:31 Hello world *)

clearLog[]
showLog[]
(* crickets *)
rm -rf
  • 88,781
  • 21
  • 293
  • 472
1

This is already built-in; Print directs all output to the $Output channel where a channel is just a list of open streams. So, to redirect it, simply

oldout = $Output;
    strm = OpenAppend["filename "<>DateString[]<>".txt"];    
    $Output = {strm};

and to switch it back

$Output = oldout;
Close[strm];

Assuming you just want an environment where Print is re-directed, you could do something like this

ClearAll[RedirectPrint];
SetAttributes[RedirectPrint, HoldRest];
Options[RedirectPrint] = {OpenMethod -> OpenAppend};
RedirectPrint[filename_String, body_, opts:OptionsPattern[]] :=
 Block[{old, strm, open},
  open = OptionValue[OpenMethod]; (* needs error checking here *)
  old = $Output;
      Internal`WithLocalSettings[
        strm = open[filename];
        $Output = {strm},
    body,
    $Output = old;
    Close[strm]
  ]
 ]
Mike Honeychurch
  • 37,541
  • 3
  • 85
  • 158
rcollyer
  • 33,976
  • 7
  • 92
  • 191