I want to display log files on my desktop from multiple sources.
I've been able to get the file displayed but it only shows the stat of the file, how can I get it to auto scroll to the end of the file?
It is currently March 29th, 2024, 12:35 pm
Displaying log files
-
- Posts: 1
- Joined: July 3rd, 2016, 2:58 am
-
- Developer
- Posts: 22628
- Joined: April 19th, 2009, 11:02 pm
- Location: Fort Hunt, Virginia, USA
Re: Displaying log files
Not sure Rainmeter is the best tool for this. First, there is nothing in Rainmeter proper that is gong to "tail" a file. You are going to need to either write some (fairly trivial) Lua, or use some external utility combined with the RunCommand plugin to display the "last N" lines of a file. However, second, since any such effort will involve reading the entire file into some buffer to get to the last lines of the file, and on EVERY update, it will be hugely inefficient. That may be ok on a small file, but if the log file gets large, there is gong to be just enormous amount of CPU use and disk thrashing.
Maybe someone will have some epiphany that I'm not seeing right off, but I'm hesitant to recommend this as by their nature log files tend to get quite large over time.
Maybe someone will have some epiphany that I'm not seeing right off, but I'm hesitant to recommend this as by their nature log files tend to get quite large over time.
-
- Developer
- Posts: 22628
- Joined: April 19th, 2009, 11:02 pm
- Location: Fort Hunt, Virginia, USA
Re: Displaying log files
If you want the Lua approach, it's like this:
TailFle.ini
TailFile.lua
So that causes my CPU usage for Rainmeter to jump from 2% to about 14% for a half-a-second or so every 5 seconds. Granted, that example log file is 450,000 lines long, but as I said, the nature of log files is that they tend to get large over time.
I could probably live with this approach on a single relatively small log file, but it would be a problem if the file gets huge, and compounded if you are reading in multiple log files in multiple skins.
A real "tail" capability would probably need to be a plugin. It would need to open the file in a parent, keep it open, and treat it as a "stream" which it just outputs to child measures. Nothing currently in Rainmeter or even externally is going to be efficient enough for this in my opinion.
TailFle.ini
Code: Select all
[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1
MouseScrollDownAction=[!Refresh]
[Metadata]
Name=TailFile
Author=JSMorley
Information=Example of using Lua to "tail" a number of lines from a text file like a log or to-do list.||Simply set [Variables] to control which file is read, the number of lines, the width of the skin, and the interval in seconds.
License=Creative Commons Attribution-Non-Commercial-Share Alike 3.0
Version=Jan 31, 2015
[Variables]
FileToRead=#@#TailFile.log
LinesToTail=5
SkinWidth=350
SecondsBetweenRead=5
ChildPrefix=MeasureLine
[MeasureTail]
Measure=Script
ScriptFile=TailFile.lua
UpdateDivider=#SecondsBetweenRead#
[MeasureLine1]
Measure=String
UpdateDivider=-1
[MeasureLine2]
Measure=String
UpdateDivider=-1
[MeasureLine3]
Measure=String
UpdateDivider=-1
[MeasureLine4]
Measure=String
UpdateDivider=-1
[MeasureLine5]
Measure=String
UpdateDivider=-1
[StyleText]
X=5
Y=5R
W=(#SkinWidth# - 20)
H=16
FontSize=11
Padding=5,5,5,5
ClipString=1
AntiAlias=1
[StyleOdd]
FontColor=255,255,255,255
SolidColor=40,50,60,255
[StyleEven]
FontColor=230,230,230,255
SolidColor=60,70,80,255
[MeterBack]
Meter=Image
W=(#SkinWidth#)
H=([MeterLine#LinesToTail#:Y]+[MeterLine#LinesToTail#:H]+5)
SolidColor=10,20,30,255
DynamicVariables=1
LeftMouseUpAction=#FileToRead#
[MeterHeaderLeft]
Meter=String
X=5
Y=5
FontSize=11
FontColor=255,255,255,255
StringStyle=Bold
AntiAlias=1
Text=TailFile
[MeterHeaderRight]
Meter=String
X=(#SkinWidth# - 10)
Y=0r
FontSize=11
FontColor=255,255,255,255
StringAlign=Right
AntiAlias=1
Text=#LinesToTail# of [MeasureTail] lines
DynamicVariables=1
[MeterLine1]
Meter=String
MeasureName=MeasureLine1
MeterStyle=StyleText | StyleOdd
Y=10R
[MeterLine2]
Meter=String
MeasureName=MeasureLine2
MeterStyle=StyleText | StyleEven
[MeterLine3]
Meter=String
MeasureName=MeasureLine3
MeterStyle=StyleText | StyleOdd
[MeterLine4]
Meter=String
MeasureName=MeasureLine4
MeterStyle=StyleText | StyleEven
[MeterLine5]
Meter=String
MeasureName=MeasureLine5
MeterStyle=StyleText | StyleOdd
Code: Select all
function Initialize()
fileToRead = SKIN:GetVariable('FileToRead')
linesToTail = SKIN:GetVariable('LinesToTail')
childPrefix = SKIN:GetVariable('ChildPrefix')
end
function Update()
local inputFile = io.open(fileToRead, 'r')
local linesTable = {}
for line in inputFile:lines() do
table.insert (linesTable, line);
end
io.close(inputFile)
for i = 1, linesToTail do
SKIN:Bang('!SetOption', childPrefix..i, 'String', linesTable[(#linesTable + 1) - i])
SKIN:Bang('!UpdateMeasure', childPrefix..i)
end
SKIN:Bang('!UpdateMeter', '*')
SKIN:Bang('!Redraw')
return #linesTable
end
I could probably live with this approach on a single relatively small log file, but it would be a problem if the file gets huge, and compounded if you are reading in multiple log files in multiple skins.
A real "tail" capability would probably need to be a plugin. It would need to open the file in a parent, keep it open, and treat it as a "stream" which it just outputs to child measures. Nothing currently in Rainmeter or even externally is going to be efficient enough for this in my opinion.
You do not have the required permissions to view the files attached to this post.
-
- Developer
- Posts: 2674
- Joined: November 24th, 2011, 1:42 am
- Location: Utah
Re: Displaying log files
You could probably drop your CPU usage down by starting at the end of the file and searching backwards...something like this:jsmorley wrote:So that causes my CPU usage for Rainmeter to jump from 2% to about 14% for a half-a-second or so every 5 seconds.
Code: Select all
function Initialize()
fileToRead = SKIN:GetVariable('FileToRead')
linesToTail = tonumber(SKIN:GetVariable('LinesToTail'))
childPrefix = SKIN:GetVariable('ChildPrefix')
end
function Update()
local inputFile = io.open(fileToRead, 'r')
local text, ch
local pos = -1
local i = 1
repeat
inputFile:seek("end", pos - 1)
ch = inputFile:read(1)
if ch == '\n' then
text = inputFile:read(-pos)
if text ~= nil then
SKIN:Bang('!SetOption', childPrefix..i, 'String', string.match(text, "^(.-)\n"))
SKIN:Bang('!UpdateMeasure', childPrefix..i)
i = i + 1
pos = pos - 1
end
end
pos = pos - 1
until (i > linesToTail)
io.close(inputFile)
SKIN:Bang('!UpdateMeter', '*')
SKIN:Bang('!Redraw')
return 0
end
-Brian
-
- Developer
- Posts: 22628
- Joined: April 19th, 2009, 11:02 pm
- Location: Fort Hunt, Virginia, USA
Re: Displaying log files
That is certainly orders of magnitude more efficient Brian. Really nice.
I'd probably only add one thing:
The lineText = string.gsub(lineText, '\r', '') is simply to strip off any \r (carriage return) characters from the string you return. While a \r without \n has no meaning in a meter for display purposes, I guess it might impact an IfMatch option or something.
As you can see I also prefer more descriptive variable names, but that is of no real consequence. Mostly just a compulsion to try and help someone else using my code understand what is going on.
I'd probably only add one thing:
Code: Select all
function Initialize()
fileToRead = SKIN:GetVariable('FileToRead')
linesToTail = tonumber(SKIN:GetVariable('LinesToTail'))
childPrefix = SKIN:GetVariable('ChildPrefix')
end
function Update()
local inputFile = assert(io.open(fileToRead, 'r'))
local singleChar, lineText
local bytePosition = -1
local i = 1
repeat
inputFile:seek('end', bytePosition - 1)
singleChar = inputFile:read(1)
if singleChar == '\n' then
lineText = inputFile:read(-bytePosition)
if lineText then
lineText = string.gsub(lineText, '\r', '')
SKIN:Bang('!SetOption', childPrefix..i, 'String', string.match(lineText, '^(.-)\n'))
SKIN:Bang('!UpdateMeasure', childPrefix..i)
i = i + 1
bytePosition = bytePosition - 1
end
end
bytePosition = bytePosition - 1
until (i > linesToTail)
io.close(inputFile)
SKIN:Bang('!UpdateMeter', '*')
SKIN:Bang('!Redraw')
return 0
end
As you can see I also prefer more descriptive variable names, but that is of no real consequence. Mostly just a compulsion to try and help someone else using my code understand what is going on.
-
- Developer
- Posts: 22628
- Joined: April 19th, 2009, 11:02 pm
- Location: Fort Hunt, Virginia, USA
Re: Displaying log files
I have posted a .rmskin of this work by Brian and myself here:
https://forum.rainmeter.net/viewtopic.php?p=108002#p108002
https://forum.rainmeter.net/viewtopic.php?p=108002#p108002
-
- Developer
- Posts: 2674
- Joined: November 24th, 2011, 1:42 am
- Location: Utah
Re: Displaying log files
Thanks for pointing this out. It didn't seem to matter in the context of this, but it is good you pointed it out in case that issue does come up.jsmorley wrote:The lineText = string.gsub(lineText, '\r', '') is simply to strip off any \r (carriage return) characters from the string you return. While a \r without \n has no meaning in a meter for display purposes, I guess it might impact an IfMatch option or something.
A good compulsion to have in a support forum like this!jsmorley wrote:As you can see I also prefer more descriptive variable names, but that is of no real consequence. Mostly just a compulsion to try and help someone else using my code understand what is going on.
-Brian