Login ProductsSalesSupportDownloadsAbout |
Home » Technical Support » DBISAM Technical Support » Support Forums » DBISAM General Discussion » View Thread |
Messages 1 to 1 of 1 total |
Using UDP Packets for Remote Logging |
Thu, Aug 10 2006 11:11 AM | Permanent Link |
"Johnnie Norsworthy" | While playing around with some low level socket stuff recently, I came
across a new extremely helpful friend that I have been using for a couple of weeks now. It is so very useful to me that I thought I'd share my technique with other here. Since my program uses the internet for a lot of its functionality, I can assume that everyone using it has an internet connection. So I use their connection to send me various events that happen throughout my software for offsite debugging, performance tuning, and general use tracking. I get a log entry when a user logs in that tells me their office, name, and email. Then if I see a problem flagged later in my log, I know who to contact. I also get log entries showing how long certain functions take, such as loading data from the server into a DevEx Grid control, or any query that I want to time. By flagging log entries with a category I can quickly select which subset I want to view. I run a simple little server program at my site using a dynamic DNS from gotdns.org. So when my program starts at my customer site, it sees my current server IP and uses that for the whole time they are connected. For some strange reason, I picked port 12007 for sending the packets. My server program uses a DevEx Grid to allow me to search, sort and stuff like that with a few controls buttons thrown in for some utility functions - nothing too complicated. The great thing about using UDP packets is that if your server is not running, it doesn't cause any problems on the client machine. The program just sends undeliverable packets. And since there is no handshaking, the packets go out in less than a half of a second, causing no program slow downs. The packets I send have: OfficeID,UserID,ProgramName,ProgramVersion,"Category" which I use to identify specific functions, and "Entry" which is any text I want to send. I just concatenate those together using "|" as a delimeter: procedure TNetLog.LogEntry(Category, Entry: string); begin // I simply disable logging if there is an error, which has never happened if Disabled then Exit; try WSocket.SendStr(OfficeID+'|'+UserID+'|'+ProgramFileName+'|'+ProgramFileVersion+'|'+ UpperCase(Category)+'|'+Entry); except Disabled := True; end; end; I have a datamodule with a single ICS TWSocket. In the datamodule create event I set up the connection information and the program version and name as fields. That is pretty much the entire code for sending packets using ICS sockets. procedure TNetLog.DataModuleCreate(Sender: TObject); begin ProgramFileName := ExtractFileName(Application.ExeName); ProgramFileVersion := utilFileVersion.GetFileVersionBuild(Application.ExeName); Disabled := False; WSocket.Proto := 'udp'; WSocket.Addr := 'myconfiguredsite.gotdns.org'; WSocket.Port := '12007'; try WSocket.Connect; // doesn't really connect, just sets up IP address from name except Disabled := True; end; end; My server program socket code is equally as simple. I have one TWSocket acting as a server called "Server" and a port opened and forwarded on my router to the server machine. The whole program including DBISAM and DevEx grids is about 4 meg. with probably less than 50 lines of real event code I wrote. I have a "CoolTrayIcon" component to minimize to the system tray and popup some balloon hints if enabled. Here is my server socket event code. Some of my utility routines are thrown in, but you can see the way it functions: procedure TfLogServer.ServerDataAvailable(Sender: TObject; ErrCode: Word); var Buffer : array [0..1023] of char; Len : Integer; Src : TSockAddrIn; SrcLen : Integer; s: string; IP,Office,User,Prog,Vers,Category,Entry,Email: String; pAt,pStart,pEnd,i: Integer; begin SrcLen := SizeOf(Src); Len := Server.ReceiveFrom(@Buffer, SizeOf(Buffer), Src, SrcLen); if Len<0 then Exit; Buffer[Len] := #0; s := StrPas(Buffer); try tableLOG.Append; IP := inet_ntoa(Src.sin_addr); Office := StrDelimNum(s,'|',1); User := StrDelimNum(s,'|',2); Prog := StrDelimNum(s,'|',3); Vers := StrDelimNum(s,'|',4); Category := StrDelimNum(s,'|',5); Entry := StrDelimNum(s,'|',6); // find the email address pAt := Pos('@',Entry); if pAt<>0 then begin i := pAt; while (i>1) and (not (Entry[i] in [' ','('])) do Dec(i); pStart := i+1; i := pAt; while (i<Length(Entry)) and (not (Entry[i] in [' ',')'])) do Inc(i); pEnd := i-1; Email := Copy(Entry,pStart,pEnd-pStart+2); end else Email := ''; tableLOG.FieldByName('IP').AsString := IP; tableLog.FieldByName('When').AsDateTime := Now; tableLOG.FieldByName('Office').AsString := Office; tableLOG.FieldByName('User').AsString := User; tableLOG.FieldByName('Program').AsString := Prog; tableLOG.FieldByName('Version').AsString := Vers; tableLOG.FieldByName('Category').AsString := Category; tableLOG.FieldByName('Entry').AsString := Entry; tableLOG.FieldByName('Email').AsString := Email; tableLOG.Post; if checkBalloon.Checked then if CoolTrayIcon.ShowBalloonHint(Office+':'+User+' - '+Category,Entry,bitInfo,10) then ; except on E:Exception do begin memoLog.Lines.Add(s+' '+E.Message); tableLOG.Cancel; end; end; end; That's it. I am using this stuff so much now I thought it would be nice to share. I was able to do all this in just a few hours and I find it incredibly useful. Now I know what functions are really being used in my program and can log timing on functions at customer sites. And when I talk to people I can say, "it's about time you got to work". -Johnnie |
This web page was last updated on Friday, April 19, 2024 at 07:09 AM | Privacy PolicySite Map © 2024 Elevate Software, Inc. All Rights Reserved Questions or comments ? E-mail us at info@elevatesoft.com |