In the upcoming release, the logging feature will have been improved in various ways. One of the new inclusions is the TkbmMWVirtualLogManager and its interface IkbmMWVirtualLogManager.
The virtual log manager can for example be used for logging select logs to a database, which this short blog will focus on.
I will in this sample, use kbmMW’s ORM to handle the database access, however any traditional database access method could have been used instead.
TdmMain = class(TDataModule) private FDBLogManager:IkbmMWVirtualLogManager; FORMLog:TkbmMWORM; public property ORMLog:TkbmMWORM read FORMLog; ...
Since I want to use the ORM for log storage handling, I need to define a class describing the storage.
[kbmMW_Table('name:myLog')] TMyLog= class private FID:kbmMWNullable<string>; FTime:TkbmMWDateTime; FInfo:kbmMWNullable<string>; FComments:kbmMWNullable<string>; public [kbmMW_Field('name:id, primary:true, generator:shortGUID',ftString,40)] property ID:kbmMWNullable<string> read FID write FID; [kbmMW_Field('name:time',ftDateTime)] [kbmMW_NotNull] property Time:TkbmMWDateTime read FTime write FTime; [kbmMW_Field('name:info',ftWideMemo)] property Info:kbmMWNullable<string> read FInfo write FInfo; [kbmMW_Field('name:comments',ftWideMemo)] property Comments:kbmMWNullable<string> read FComments write FComments; end;
The TSystemLog class needs to be registered:
initialization TkbmMWRTTI.EnableRTTI([TMyLog]); kbmMWRegisterKnownClasses([TMyLog]);
We add a method that we can call to persist the log entry. Notice that if the method is unable to persist the log due to some database issue, an error will be logged on the SystemLog, which is a standard, always existing, alternative logger in kbmMW. It will default output to debug view on Windows, or LogCat on Android.
procedure TdmMain.MyLog(const ATime:TkbmMWDateTime; const AMessage:string); var l:TMyLog; begin try l:=TMyLog.Create; try l.Time:=ATime; l.Info:=AMessage; ORMLog.Persist(l); finally l.Free; end; except on E: Exception do begin SystemLog.Error('LogSystem',E); end; end; end;
And at some point before the first time the database log will be used, the database tables needs to be prepared:
function TdmMain.PrepareLogDatabase:boolean; begin Result:=false; // Open log database. dbLog is the (in this sample, SQLite) connection pool for the database. FORMLog.OpenDatabase(dbLog); // Create tables if they are not available. // Upgrade them if they exists and needs upgrade. if not FORMLog.CreateOrUpgradeTable([TMyLog]) then begin Log.Fatal('Unable to create or upgrade log database: '+dbLog.Database+'. Server not started!'); exit; end; Result:=true; end;
And finally I define the TkbmMWVirtualLogManager which in turn calls the MyLog method whenever there is something for this logmanager to handle.
Because I want to put timestamp in a separate field in the database, I redefine the logformatter of this logmanager to only include a few select type of information.
// Prepare database oriented log manager. FDBLogManager:=TkbmMWVirtualLogManager.Create( procedure(const AType:TkbmMWLogType; const ALevel:TkbmMWLogLevel; const AOrigin:string; const ATime:TkbmMWDateTime; const AString:string) begin // Specifically do not accept messages comming from kbmMW's internals itself, // since those could be generated from the database layers, resulting in deadlock. if pos('kbmMW',AOrigin)=0 then MyLog(ATime,AString); end ); // Setup the log formatter to only include a few things in the log string. FDBLogManager.LogFormatter:=TkbmMWSimpleLogFormatter.Create; FDBLogManager.LogFormatter.Columns:=[mwlfcLogType,mwlfcLogString,mwlfcLogData]; Log.LogManager:=FDBLogManager;
Now every time you use Log.Info/Log.Error/Log.Warning/Log.Fatal or any of the other log methods, the log will be appended to the myLog table in the database.
Remember… if you like the blogs, kbmMW or any of our other products, share the word and your feelings about the products!