Write to file in real time

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
markgo
Party member
Posts: 190
Joined: Sat Jan 05, 2013 12:21 am
Location: USA

Write to file in real time

Post by markgo »

I'm trying to modify the print function to print to a text file in real time for debugging purposes. I can't get it to update the newly created text file without closing it and opening it again before each write operation. I've tried file:flush() and file:setvbuf('no') to force the log file to update, but it doesn't work for me. The bottom code works for me, but I'm just disappointed that the aforementioned methods didn't work. What am I doing wrong?

Code: Select all

local p = print
return function(filename)
	local file = io.output(filename)
	file:close()
	if #filename == 0 then print = p return end
	function print(...)
		file = io.open(filename,'a+')
		local strings = {...}
		for i = 1,#strings do
			strings[i] = tostring(strings[i])
		end
		local string  = table.concat(strings,'\t')
		p(...)
		file:write(string .. '\n')
		-- file:flush() -- flush instead of close
		file:close()
	end
end
User avatar
Boolsheet
Inner party member
Posts: 780
Joined: Wed Dec 29, 2010 4:57 am
Location: Switzerland

Re: Write to file in real time

Post by Boolsheet »

Hm, flushing works for me (Windows and Linux). I think the problem is that you are opening the file every time the function is called, instead of reusing the file handle.

Let's go through the code.

Code: Select all

local p = print
return function(filename)
I take it you're using require to load this piece of code. Is the intention to only execute the returned function once or do you plan to change the output file multiple times?

Code: Select all

   local file = io.output(filename)
   file:close()
This is a problem. You set a new default output file and then you close it. Everything using the default output with io.write, io.flush or io.close is going to error.

Code: Select all

   if #filename == 0 then print = p return end
Either this doesn't do anything or it is supposed to unhook your function, which means you intend to call it more than once.

Code: Select all

   function print(...)
A general warning: be careful when replacing the global variable print in LÖVE. The default error handler uses it and can react very sensitive if your replacement function throws an error.

Code: Select all

      file = io.open(filename,'a+')
      local strings = {...}
      for i = 1,#strings do
         strings[i] = tostring(strings[i])
      end
      local string  = table.concat(strings,'\t')
      p(...)
      file:write(string .. '\n')
      -- file:flush() -- flush instead of close
      file:close()
   end
end
This looks OK except you reopen the file every time. Consider using a closure that saves the file handle in an upvalue. Something like this.

Code: Select all

local p = print
local file

-- The function that will replace print.
-- It uses an upvalue for the file handle.
local function file_print(...)
	p(...)
	if file then
		local strings = {...}
		for i = 1,#strings do
		 strings[i] = tostring(strings[i])
		end
		local output = table.concat(strings,'\t')
		file:write(output .. '\n')
		file:flush()
	end
end

-- Returns true on success and false (and an error message) on failure.
return function(filename)
	if not filename or #filename == 0 then
		-- Close the file and reset to stdout.
		if file then
			io.output(io.stdout)
			file:close()
			file = nil
			print = p
		end
		return true
	end

	local new_file, err = io.open(filename, "a")
	if not new_file then
		-- Open failed. Probably invalid path or permission issues.
		return false, err
	end

	io.output(new_file)
	if file then
		file:close()
	end
	file = new_file
	print = file_print

	return true
end
Shallow indentations.
User avatar
markgo
Party member
Posts: 190
Joined: Sat Jan 05, 2013 12:21 am
Location: USA

Re: Write to file in real time

Post by markgo »

Ah you read my mind. I intend to execute the function multiple times for different output files. Your version is definitely nicer as it catches errors. However it's still not working for me. It seems that the file updates itself arbitrarily for me. Sometimes it updates after one print out, and sometimes it doesn't print out until several lines. Closing the love game definitely updates the file immediately. Ah well, I'll just live with it. Running Windows 7 by the way.

The new printToFile:

Code: Select all

local p = print
    local file

    -- The function that will replace print.
    -- It uses an upvalue for the file handle.
    local function file_print(...)
       p(...)
       if file then
          local strings = {...}
          for i = 1,#strings do
           strings[i] = tostring(strings[i])
          end
          local output = table.concat(strings,'\t')
          file:write(output .. '\n')
          file:flush()
       end
    end

    -- Returns true on success and false (and an error message) on failure.
    return function(filename)
       if not filename or #filename == 0 then
          -- Close the file and reset to stdout.
          if file then
             io.output(io.stdout)
             file:close()
             file = nil
             print = p
          end
          return true
       end

       local new_file, err = io.open(filename, "a")
       if not new_file then
          -- Open failed. Probably invalid path or permission issues.
          return false, err
       end

       io.output(new_file)
       if file then
          file:close()
       end
       file = new_file
       print = file_print

       return true
    end

Code: Select all

printToFile = require 'printToFile'
printToFile('C:/testing/test.txt')
output = ''

function love.keypressed(k)
	if k == 'return' or k == 'kpenter' then print(output) output = '' return end
	output = output .. k
end

function love.draw()
	love.graphics.print(output,10,10)
end
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 3 guests