{
Summary:
Abstract console interface
## $Id: console.pas,v 1.16 2004/04/14 19:35:12 druid Exp $
}
unit console;
interface
uses
SysUtils,
Contnrs,
dtypes;
type
GConsoleWriter = class
public
procedure write(timestamp : integer; const text : string; debugLevel : integer = 0); virtual; abstract;
end;
GConsoleLogWriter = class(GConsoleWriter)
private
logFile : textfile;
public
constructor Create(const moduleName : string);
destructor Destroy(); override;
procedure write(timestamp : integer; const text : string; debugLevel : integer = 0); override;
end;
GConsole = class(GSingleton)
private
writers : GDLinkedList;
history : GDLinkedList;
synchronizer : TMultiReadExclusiveWriteSynchronizer;
public
constructor actualCreate(); override;
destructor actualDestroy(); override;
published
procedure write(const text : string; debugLevel : integer = 0);
procedure attachWriter(writer : GConsoleWriter);
procedure detachWriter(writer : GConsoleWriter);
procedure fetchHistory(callback : GConsoleWriter; max : integer = 0);
function fetchHistoryTimestamp(callback : GConsoleWriter; timestamp : integer) : integer;
end;
procedure writeConsole(const text : string; debugLevel : integer = 0);
implementation
uses
DateUtils,
fsys;
type
GConsoleHistoryElement = class
private
_timestamp : integer;
_text : string;
_debugLevel : integer;
public
property timestamp : integer read _timestamp write _timestamp;
property text : string read _text write _text;
property debugLevel : integer read _debugLevel write _debugLevel;
end;
const
{ Maximum number of items in the console history }
CONSOLE_HISTORY_MAX = 200;
var
cons : GConsole;
{ GConsole constructor }
constructor GConsole.ActualCreate();
begin
writers := GDLinkedList.Create();
history := GDLinkedList.Create();
synchronizer := TMultiReadExclusiveWriteSynchronizer.Create();
end;
{ GConsole destructor }
destructor GConsole.ActualDestroy();
begin
writers.clear();
history.clear();
writers.Free();
history.Free();
synchronizer.Free();
end;
{ Attach a GConsoleWriter object to the console }
procedure GConsole.attachWriter(writer : GConsoleWriter);
begin
writers.add(writer);
end;
{ Detach a GConsoleWriter object from the console }
procedure GConsole.detachWriter(writer : GConsoleWriter);
begin
writers.remove(writer);
end;
{ Write a message to the console }
procedure GConsole.write(const text : string; debugLevel : integer = 0);
var
he : GConsoleHistoryElement;
iterator : GIterator;
writer : GConsoleWriter;
begin
// lock write
synchronizer.BeginWrite();
try
if (history.size() >= CONSOLE_HISTORY_MAX) then
begin
he := GConsoleHistoryElement(history.head.element);
history.remove(history.head);
he.Free();
end;
he := GConsoleHistoryElement.Create();
he.timestamp := DateTimeToUnix(Now);
he.text := text;
he.debugLevel := debugLevel;
history.add(he);
iterator := writers.iterator();
while (iterator.hasNext()) do
begin
writer := GConsoleWriter(iterator.next());
writer.write(he.timestamp, he.text, he.debugLevel);
end;
iterator.Free();
finally
// unlock write
synchronizer.EndWrite();
end;
end;
{ Fetch (up to) max items from the history and feed them to callback }
procedure GConsole.fetchHistory(callback : GConsoleWriter; max : integer = 0);
var
iterator : GIterator;
he : GConsoleHistoryElement;
count : integer;
begin
// lock read
synchronizer.BeginRead();
try
iterator := history.iterator();
count := 0;
while (iterator.hasNext()) do
begin
he := GConsoleHistoryElement(iterator.next());
callback.write(he.timestamp, he.text);
inc(count);
if (max > 0) and (count >= max) then
break;
end;
iterator.Free();
finally
// unlock read
synchronizer.EndRead();
end;
end;
{ Enumerate all items with item.timestamp >= timestamp, return last known timestamp }
function GConsole.fetchHistoryTimestamp(callback : GConsoleWriter; timestamp : integer) : integer;
var
iterator : GIterator;
he : GConsoleHistoryElement;
begin
// lock read
synchronizer.BeginRead();
Result := timestamp;
try
iterator := history.iterator();
while (iterator.hasNext()) do
begin
he := GConsoleHistoryElement(iterator.next());
Result := he.timestamp;
if (he.timestamp <= timestamp) then
continue;
callback.write(he.timestamp, he.text);
end;
iterator.Free();
finally
// unlock read
synchronizer.EndRead();
end;
end;
procedure writeConsole(const text : string; debugLevel : integer = 0);
begin
cons.write(text, debugLevel);
end;
{ GConsoleLogWriter constructor }
constructor GConsoleLogWriter.Create(const moduleName : string);
begin
inherited Create();
{ open a standard log file, filename is given by current system time }
AssignFile(logFile, translateFileName('logs\' + moduleName + '.log'));
{$I-}
Append(logFile);
if (IOResult <> 0) then
Rewrite(logFile);
{$I+}
if (IOResult <> 0) then
writeConsole('Could not open logfile');
end;
{ GConsoleLogWriter destructor }
destructor GConsoleLogWriter.Destroy();
begin
if (TTextRec(logfile).mode = fmOutput) then
CloseFile(LogFile);
inherited Destroy();
end;
{ Writes to logfile }
procedure GConsoleLogWriter.write(timestamp : integer; const text : string; debugLevel : integer = 0);
begin
if (TTextRec(logfile).mode = fmOutput) then
begin
system.writeln(logfile, '[' + FormatDateTime('yyyymmdd hh:nn:ss', UnixToDateTime(timestamp)) + '] ' + text);
system.flush(logfile);
end;
end;
initialization
cons := GConsole.Create();
finalization
FreeAndNil(cons);
end.