Chat Logging in PHP / Linux

Discussion in 'Starbound Modding' started by Ayeso, Dec 6, 2013.

  1. Ayeso

    Ayeso Scruffy Nerf-Herder

    I thought I might share this since it took me a few minutes to look around and figure out how.

    I setup a basic chat logging and output script. See here for an example. http://pielayer.getonmylevel.ca/starbound.php

    Create a Cron job that does the following.

    Code:
    grep "Info:  <" /home/linux64/starbound_server.log > /tmp/chat.txt
    Then create a PHP page with the following.
    PHP:
    <?PHP

    $file_handle 
    fopen("/tmp/chat.txt""r");

    while (!
    feof($file_handle) ) {
    $line_of_text fgets($file_handle);
    $parts explode('Info:  <'$line_of_text);
    print 
    $parts [0] . $parts [1]. "<BR>";

    }

    fclose($file_handle);

    ?>
     
    Ghoul159 likes this.
  2. Halaster

    Halaster Space Spelunker

    Good tip. :)

    I expanded upon the php page a bit to give it a better layout.
    It will also start with the scrollbar at the bottom of the chat log instead of the top automatically.
    It will check for mouse movement. If there has been no mouse movement for more than 4 seconds, the page will refresh loading the latest chat. Added this over a pure refresh to make sure if someone is scrolling through chat the page does not get reloaded and they end up back at the bottom again.

    The scripts are commented and values can be easily edited. In addition the CSS at the top is prepared for use with an additional column to the left, as well as a footer, so the page can be expanded if one so chooses. So the page can be used as is, expanded, or stripped down to be more bare bones.

    Code:
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Netheril Starbound Server Page</title>
    <style type="text/css">
    
    body{
    margin:0;
    padding:0;
    line-height: 1.5em;
    }
    
    b{font-size: 110%;}
    em{color: red;}
    
    #maincontainer{
    width: 840px; /*Width of main container*/
    margin: 0 auto; /*Center container on page*/
    }
    
    #topsection{
    background: #FFFFFF;
    color: #000000;
    height: 90px; /*Height of top section*/
    }
    
    #topsection h1{
    margin: 0;
    padding-top: 15px;
    }
    
    #contentwrapper{
    float: left;
    width: 100%;
    }
    
    #contentcolumn{
    margin-left: 0px; /*Set left margin to LeftColumnWidth*/
    margin-bottom: 0px;
    overflow-y:scroll;
    height: 600px;
    border-style:inset;
    border-width:4px;
    }
    
    #leftcolumn{
    float: left;
    width: 200px; /*Width of left column*/
    margin-left: -840px; /*Set left margin to -(MainContainerWidth)*/
    background: #C8FC98;
    }
    
    #footer{
    clear: left;
    width: 100%;
    background: black;
    color: #FFF;
    text-align: center;
    padding: 0px 0;
    }
    
    #footer a{
    color: #FFFF80;
    }
    
    .innerdiv{
    margin: 10px; /*Margins for inner DIV inside each column (to provide padding)*/
    margin-top: 0;
    }
    </style>
    </head>
    
    <body onload="refreshOnIdle();" onmousemove="haltTimer();">
    
    <script type="text/javascript"> /* This script causes the page to check for mouse moment, and if there has been none, load new chat automatically every 5 seconds. */
    var lastEvent = new Date().getTime();
    var threshold = 4000; /*How long to wait for mouse movement. */
    function doReload(){
            var now = (new Date()).getTime();
            if (now - lastEvent > threshold){
                    window.location.reload(true);
            }
    }
    
    document.body.onmousemove=function(){
            lastEvent = (new Date()).getTime();
    };
    window.setInterval(doReload, 1000);
    </script>
    
    <div id="maincontainer">
    
    <div id="topsection"><div class="innerdiv"><h1>Insert your page header here</h1></div></div>
    
    <div id="contentwrapper">
    <div id="contentcolumn">
    <div class="innerdiv">
    
    <?PHP
    
    $file_handle = fopen("/tmp/chat.txt", "r");
    
    while (!feof($file_handle) ) {
    $line_of_text = fgets($file_handle);
    $parts = explode('Info:  <', $line_of_text);
    print $parts [0] . $parts [1]. "<BR>";
    }
    
    fclose($file_handle);
    
    ?>
    
    </div>
    </div>
    
    <script type="text/javascript"> /*This script causes the scrollbar to automatically go to the bottom of chat on page load/reload.*/
        var objDiv = document.getElementById("contentcolumn");
        objDiv.scrollTop = objDiv.scrollHeight;
    </script>
    
    </div>
    
    <div id="footer"></div>
    
    </div>
    </body>
    </html>
     
    danks_ likes this.
  3. Ayeso

    Ayeso Scruffy Nerf-Herder

    Woah nice! I whipped mine up real quick just so I could see what was going on but this is awesome. I plan to shamefully steal this!
     
  4. kavex

    kavex Space Penguin Leader

    very kool thread
     
  5. Halaster

    Halaster Space Spelunker

    Starting with your base work this is what I ended up making for my server. Final page.
     
  6. LiLChris

    LiLChris Void-Bound Voyager

    Wow nice work, will probably stick it on my forums. :D
     
  7. Halaster

    Halaster Space Spelunker

    I made further modifications, changing the initial grep script. I found that the way it is currently setup, it wipes out all of the old chat data anytime the server log is recreated, due to a server restart or so on.

    If you want to keep all chat logging you can use this instead:

    Code:
    while inotifywait -e modify /root/steam/starbound/linux64/starbound_server.log
    do
            tail -1 /root/steam/starbound/linux64/starbound_server.log | grep "Info:  <" >> /log/chat.txt
    done
    
    To use this requires inotify-tools to be installed. The directories of course should be equal to your own as well.
    What this does is it watches the starbound_server.log file for changes. When it detects a change to the server file, it runs a tail on the most recent line only, and greps for chat. It then appends the chat to the /log/chat.txt file.

    Using ">" instead of ">>" does a complete replace of the existing file with new data, which is what causes the loss of old chat history. Using just grep with >> though is not a viable option either, as it will add the same data multiple times. So you end up with a chat log with more and more duplicate entries.

    Using this method I put together above will always make sure to only add new data to the bottom of the chat.txt file, but will not erase the current data.
    You would not want to run this as a cronjob either, but actually place it inside of a screen and run it.
     
    Last edited: Dec 9, 2013
  8. Halaster

    Halaster Space Spelunker

    To get it to automatically start place that code above into a file named what you like. I named mine logchat.

    Make it executable:
    chmod +x logchat

    Create a second file named whatever you like, I named mine starbound_chat_logging.

    Inside that file place the following, making sure to use your own directory structure and file name:

    Code:
    cd /root/steam/starbound
    screen -S ChatLog -d -m ./logchat
    Make it executable:
    chmod +x starbound_chat_logging

    Copy that file into /etc/init.d:
    cp /root/steam/starbound/starbound_chat_logging /etc/init.d/starbound_chat_logging

    Make the script auto-start with your server:
    update-rc.d starbound_chat_logging defaults


    The end. That will make the chat logging automatically start with your server inside of a screen named ChatLog.
    You can attach to the screen at any time to see what it is doing with:
    screen -r ChatLog

    You can kill the screen with:
    CTRL+C

    You can detach from the screen by pressing:
    CTRL+A then CTRL+D
     
  9. Halaster

    Halaster Space Spelunker

    There is actually an issue with the PHP but I have not been able to figure out a solution to fix it.
    With this PHP code in a matter of days my error log was filed with over 17gb of error messages.
    It appears as if the explode is causing a blank NULL value at the end which does not actually exist.
    This results in the error log being filled with: "PHP Notice: Undefined offset: 1 in /var/www/index.php on line 208"

    I can fix it multiple different ways without using explode, but then I do not get the names of the people who are chatting, only their messages, and the preceding "Info:". I seem to be having trouble getting the <name> data to display, even though its on the same line.
    Does anyone have any tips on how to address this?
     
  10. Halaster

    Halaster Space Spelunker

    I believe I figured it out.

    With a slight modification of the PHP, it now properly handles null values and no longer throws out warnings into the "/var/log/apache2/error.log" file.
    Before:
    PHP:
    <?PHP

    $file_handle 
    fopen("/tmp/chat.txt""r");

    while (!
    feof($file_handle) ) {
    $line_of_text fgets($file_handle);
    $parts explode('Info:  <'$line_of_text);
    print 
    $parts [0] . $parts [1]. "<BR>";

    }

    fclose($file_handle);

    ?>
    After:
    PHP:
    <?PHP

    $file_handle 
    fopen("/tmp/chat.txt""r");

    while (!
    feof($file_handle) ) {
    $line_of_text fgets($file_handle);
    $parts array_pad(explode("Info:  <"$line_of_text), 2null);
    print 
    $parts [0] . $parts [1]. "<BR>";

    }

    fclose($file_handle);

    ?>
     
  11. kashiro

    kashiro Space Hobo

    Any chance someone here could quickly write the code for windows equivalent of grep (findstr) ? :p
     
  12. Halaster

    Halaster Space Spelunker

    Not sure how to do that myself unfortunately.
     
  13. The_Fool76

    The_Fool76 Void-Bound Voyager

    Personally I'd use preg_match() in your PHP script instead of having grep involved at all.
     
  14. Halaster

    Halaster Space Spelunker

    Doesn't that depend on all of the data you want to display being present in the log file ?

    grep is being used to parse the chat data out of the server log which is wiped on a regular basis, into its own file, that can then be displayed with PHP and is never cleared automatically. At least in my modification. The original post of course does wipe the file, so this suggestion would be good.

    To use preg_match() and no grep at all, then the chat log would only parse the current file, and lose all historical chat. Unless I am misunderstanding the suggestion. This current way also ends up with a text file that includes all of the chat.
     
    Last edited: Dec 12, 2013
  15. The_Fool76

    The_Fool76 Void-Bound Voyager

    So replace the grep with a copy? If your log file is being cleared you should probably have a backup of it anyway.

    I confess, I've not yet poked the server side of things so I wasn't aware that the log file got cleared. How often is it cleared? Is it periodically or only on a server restart?
     
  16. Halaster

    Halaster Space Spelunker

    Why replace the grep with a copy though, the only information needed is chat logging, not all the extra information that is pretty much useless warnings right now.

    The base log file right now has no time stamps, and tons of virtually useless information. Using PHP to parse through the entire log file instead of a file that contains only the chat text seems inefficient. Especially for a server that has ran for a long time.

    It is filled with information like this:
    Code:
    Warn: Perf: UniverseServer::run.innerloop millis: 211
    Info: Connection received from: 0000:0000:0000:0000:0000:0000:0000:0000
    Info: Sending Handshake Challenge
    Warn: Perf: UniverseServer::handleQueuedConnection.handshake millis: 157
    Info: Loading ship world received from client <3>
    Warn: Object boosterflame does not have a price set
    Warn: Object smallboosterflame does not have a price set
    Warn: Object teleporter does not have a price set
    Warn: Object hylotlshiplocker does not have a price set
    Warn: Object apexfuelhatch does not have a category set
    Warn: Object apexfuelhatch does not have a price set
    Warn: Object apexcaptainschair does not have a price set
    Info: Loading world db for world alpha:27066771:75683948:-10297482:11:5
    Warn: Object flowerblue does not have a price set
    Warn: Perf: UniverseServer::createWorld millis: 190
    Info: Client <3> connected
    Warn: Perf: UniverseServer::run.innerloop millis: 238
    Warn: Perf: UniverseServer::doTriggeredStorage millis: 157
    Warn: Perf: UniverseServer::run.innerloop millis: 258
    Warn: Object wildcarrotseed does not have a price set
    Warn: Perf: UniverseServer::run.innerloop millis: 239
    Warn: Object wildcarrotseed does not have a price set
    Warn: Perf: UniverseServer::doTriggeredStorage millis: 167
    Warn: Perf: UniverseServer::run.innerloop millis: 268
    Warn: Object wildcarrotseed does not have a price set
    Warn: Object wildcarrotseed does not have a price set
    Warn: Object wildcarrotseed does not have a price set
    Warn: Perf: UniverseServer::run.innerloop millis: 242
    Warn: Perf: UniverseServer::run.innerloop millis: 216
    Warn: Object smallboosterflame does not have a price set
    Warn: Object apexcaptainschair does not have a price set
    Warn: Object apexfuelhatch does not have a category set
    Warn: Object apexfuelhatch does not have a price set
    Warn: Object boosterflamehuman does not have a price set
    Warn: Object apexshiplocker does not have a price set
    
    Just tons and tons of useless info.
    No names on the client connects, no time stamps, so on.
     
  17. Lila

    Lila Phantasmal Quasar

    You are not escaping html characters. I can go on your server, type some nasty javascript in chat and have it run on your web page.
    run the chat through htmlspecialchars() before printing it. This is extremely important.
     
  18. Netist

    Netist Orbital Explorer

    Why in god's name are you doing this in PHP if you're going to use the shell anyway? If you're going to parse the whole thing in your PHP, fair enough, but what you're doing baffles me.

    Code:
    tail -F starbound_server.log | sed -n 's/^Info:\s\+\(<[^>]\+>\)/\1/ p'
    If you're concerned about your log being too big, write a script to rotate it properly. It's not hard. Hell, just use logrotate.

    And escape your damn input. Users are evil and cannot be trusted. Servers like this are what my friends and I call "easy targets".
     
    Last edited: Dec 12, 2013
    The_Fool76 likes this.
  19. Halaster

    Halaster Space Spelunker

    Thanks for the tips, adding the escaping of html characters and switching from an ionotifywait to the more simple tail command.
    I still want to have all of the chat in an individual text file on the server though, so I am going to output it to a separate file and read that with the php.

    The log being too big is not really a worry for me. I just want a single file that contains all of the historical chat from the server, and just the chat.

    My server is only given to direct, local, friends, not random folks on the internet who might come in and run javascript in chat, but since I have posted on here I suppose that some folks could log in and do things.

    Such a harsh tone in the post, heh.
     
  20. The_Fool76

    The_Fool76 Void-Bound Voyager

    Yea that's the route I was just heading down myself. Though my sed script is slightly more complex.

    Code:
    tail -F starbound_server.log | sed -n '/^Info: / {
    s/&/\&amp;/ig
    s/</\&lt;/ig
    s/>/\&gt;/ig
    x
    s/^.*$/date +"%G-%m-%d %H:%M:%S | "/
    e
    G
    s/\n//
    p
    x
    }
    '
    
    This will attach a time stamp to each line and escape the '<>&' characters so they don't cause issues on a web page.
     

Share This Page