Writing log statements to standard output with Matlab
We're starting Matlab from our Jenkins buildserver. As the build may take some time it would be nice to get some log-outputs while matlab is running. Is there a way to print text to standard output? disp, fprintf and java.lang.System.out.printline only write to t开发者_JAVA技巧he matlab console, not to standard output.
Using a logfile or a pipe won't help, as Jenkins only reads from standard-output during a build step.
How can we write log-statements to the standard output while matlab is running?
EDIT: We're running Matlab 2010b on Windows
Depending what you are doing with Matlab you could probably launch it in command line without GUI. I used this on a server and it behaves pretty much like a shell script and writes to standards outputs.
See the startup options.
I used the following:
/path/to/matlab -nojvm -nodisplay -nosplash -nodesktop -r /path/to/mfile
EDIT: forgot to mention one very important little detail, place an exit
command at the end of your mfile or Matlab will hang there waiting.
It seems that the combination of -wait
and -log
(not -logfile
) clones the command window output to the parent console's stdout, but only if you call the MATLAB executable in [MATLABROOT]\bin, not [MATLABROOT]\bin\win64 (the subdirectory for current arch).
Tested on Windows with R2015b and R2016b:
C:\MATLAB\bin\matlab.exe -wait -log
NOT
C:\MATLAB\win64\bin\matlab.exe -wait -log
Remember to put an exit
/quit
in your script if you are running with -r
.
The only trouble is that I can't seem to find any documentation for the -log
option! Meh.
There don't seem to be any good ways to do this from within MATLAB. The easiest way I can think of doing this is by using a shell script. You could write a small shell script which would simply print any input to stdout, and then call that shell script from within matlab using the unix
(or system
) commands. Jenkins should be able to read the command-line output of the script and work with that.
I figured out a way to do this and am also doing it for Jenkins Matlab interface on windows.
Basic idea is that you will use diary command, but then tail -f the file, but you need a smart way to kill the tail command if you open multiple matlab instances because there will be name collisions. So the method I'm using is to name the file log.txt where the PID used is MATLAB's PID it is using when it opens.
There is an undocumented feature in MATLAB that allows you to get its PID. So now, both your batch file and MATLAB know the PID without having to read/write to a random text file that will get messy when executing multiple jobs. So the PID you use that as your unique identifier. The PID of "tail -f" is also used by MATLAB to kill tail -f to make the batch file die and is found by MATLAB using the commandline details associated with the process invocation since it uses again the unique PID log file name.
This uses some wmic commands and needs Windows Vista/7 or above. With XP you probably have to work harder to get the process ID's but should be still possible.
Here is what to do:
1) Get gnu awk for windows: http://gnuwin32.sourceforge.net/packages/gawk.htm
2) Get tail.exe from windows resource kit: http://www.microsoft.com/en-us/download/details.aspx?id=17657
3) Make sure tail and awk are in your path (the windows resourece kit I don't think automatically puts them in the path)
3) Create a batch file called matlabrun.bat as follows, (note: you need the @echo off, also the entire command is quite long, scroll right..)
@echo off
wmic process call create "c:\matlab\bin\win64\matlab.exe -r \"cd('c:\jenkins\workspace\test'); workdir=pwd; outpath=[pwd '\output'] ; try; run('C:\MATLAB\work\test_run'); end; quit; \" " | findstr ProcessId | awk "{print $3}" | awk -F";" "{ print $1 }"
4) Create another batch file called run.bat with:
for /f %%i in ('matlabrun.bat') do (
echo MATLAB Log... > log%%i.txt
tail -f log%%i.txt
set logfilename=log%%i.txt
goto next
)
:next
del /f %logfilename%
5) The run.bat file will execute matlabrun.bat and since -wait is not passed, matlab will immediately return to the command line and execute the tail -f command. That will block the batch file from completing until you kill it. matlabrun.bat returns the PID of matlab.
6) Another important note: since you are using "wmic process create" which will provide you with a PID that MATLAB is using, but will default to a working directory of c:\windows\system32. So that is why I pass the work directory to matlab. wmic process create is also a bit particular about what parameters you put into your command string for matlab to run. So it appears to have a problem with using commas in your command string. So I suggest not to use those, or figure out how to escape them (it might be that ^, works, but I just removed my commas anyway in my matlab run command).
6) The "test_run.m" file contains the following code to write to the correct log file and to kill correct tail -f instance.
matlabpid=feature('getpid');
filename=['log',num2str(matlabpid),'.txt'];
filenamefull=[workdir,'\',filename];
diary(filenamefull);
disp('Script starting...')
%%% put your code here %%%
disp('Script completed...');
diary off;
%%% FIND PID of tail.exe and kill it
%%% by using the name of the log file in the process command line
[a,b]=dos(['wmic process get Commandline,ProcessId']);
C=textscan(b,'%s','delimiter','\n');C=C{1};
for jj=1:size(C,1),
if strfind(C{jj},filename),
D=textscan(C{jj},'%s');D=D{1};
dos(['taskkill /f /pid ',D{4}]) %kills tail.exe which is the log watcher
break
end
end
7) You start it by doing run.bat. It will go and execute matlab, then start tailing the output while MATLAB runs in real-time. Then when done it will delete the log file.
8) My directory structure / files are in these locations (I'm using win7 64bit):
c:\jenkins\workspace\test\tail.exe
c:\jenkins\workspace\test\awk.exe
c:\jenkins\workspace\test\matlabrun.bat
c:\jenkins\workspace\test\run.bat
c:\matlab\work\test_run.m
c:\matlab\bin\win64\matlab.exe
If you are using 32bit matlab, point it to the win32 directory. To get the correct PID, you need to specify the actualy matlab.exe binary in the win32 or win64 directory.
You can do this by pointing the -logfile option to the Jenkins log file. Something like the following:
"C:\path\to\matlab.exe" "-r" "functionToRun" "-logfile" "%JENKINS_HOME%\jobs\%JOB_NAME%\builds\%BUILD_NUMBER%\log" /wait
You can use the diary mode. Not sure if it will fit your specific implementation.
http://www.mathworks.com/help/techdoc/ref/diary.html
I didn't find a real solution. Mathworks created some wrapper tool. But this will only output the results after matlab has exited. You won't get any ouput during execution.
http://www.mathworks.de/support/solutions/en/data/1-ACT3YN/index.html?product=ML&solution=1-ACT3YN
So I'll have live without real live-output...
Or try using '-logfile' option in matlab.
matlab.exe -nodisplay -nosplash -nodesktop -wait -logfile logfile.txt -r "try script.m ;catch err; disp(err.message); end ; exit"
I prefer using bash (Execute shell) in Jenkins, then you can tail the log-file while matlab is running.
matlab.exe <...> &
matpid=$!
tail -f logfile.txt &
tailpid=$!
wait $matpid
matexit=$?
kill $tailpid
sleep 1 # Just to make sure kill is done before Jenkins step ends and no zombie processes
exit $matexit
精彩评论