Own Logger in Middleware

I think I get confused with the documentation and the wizards. 
I want use most things without visual design e.g. create middleware in code.

But first I have a datamodule with 

type
  TdmServer = class(TDataModule)
    SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher;
    XDataServer: TXDataServer;
    XDataConnectionPool: TXDataConnectionPool;

I want add a TLoggingMiddleware with my own Logger;



procedure TdmServer.DataModuleCreate(Sender: TObject);
var
  Logging: TLoggingMiddleware;
begin
  XDataServer.BaseUrl := cBasisURL;
  XDataConnectionPool.Connection := dmDatabase.AureliusConnection;


  Logging := TLoggingMiddleware.Create( XDataLogger);  // XDataLogger is my own Logger


  XDataServer.Add Middleware ???


How can I add the middleware?
I'm waiting eagerly for the 14.11. training days

Greetings Thomas
Hi Thomas,

Add a generic middleware and set a handler for the event OnMiddlewareCreate. Then just create any middleware you want there, for example:



procedure TForm4.XDataServer1GenericMiddlewareCreate(Sender: TObject;
  var Middleware: IHttpServerMiddleware);
begin
  Middleware := TLoggingMiddleware.Create( XDataLogger);
end;


I'm also looking forward for TMS Training Days!

It works, thank you.

cu

New problem.
My logger works, but if I close the application, an exception Access_Violation occurs. (on dot-end at main source in CPU)
This exception only occurs if I integrate the logger via generic middleware.

Do I have to stop or release something?

It is the logger.free in finalization of logger-unit.
I stop the xdata-server und free the datamodul who owns the server.

When can I free the logger? Or how can I delte the generic middleware?

Can you please provide a minimal project that reproduces the problem? It's hard to guess what's going on without seeing any source code, thank you.

It's very simple:
If I uncomment the red line, I get an exception at program end on the last line in Main.
Exception class: $C0000005 Message c0000005 ACCESS_VIOLATION
I think, XData sends a logging message but the logger does not more exists

Main




program sngAuth;


{$APPTYPE CONSOLE}


{$R *.res}


uses
  System.SysUtils,
  sng.Logging in '..\..\allg\sng.Logging.pas',
  sng.Utilities in '..\..\allg\sng.Utilities.pas',
  sng.dmServer in '..\Allg\sng.dmServer.pas' {dmServer: TDataModule},
  sng.dmDatabase in '..\Allg\sng.dmDatabase.pas' {dmDatabase: TDataModule},
  sng.dbSession in '..\Allg\sng.dbSession.pas',
  Auth.func.Impl in 'Auth.func.Impl.pas',
  Auth.dmDatabase in 'Auth.dmDatabase.pas' {dmDatabaseAuth: TDataModule},
  sng.dbSession.Impl in '..\Allg\sng.dbSession.Impl.pas',
  sng.consts in '..\Allg\sng.consts.pas',
  sng.Filesystem in '..\..\allg\sng.Filesystem.pas',
  Auth.dmServer in 'Auth.dmServer.pas' {dmServerAuth: TDataModule},
  MessageDialogs in '..\..\allg\MessageDialogs.pas',
  sng.Server.Settings in '..\Allg\sng.Server.Settings.pas',
  Auth.dbEntities in '..\..\entities\Auth.dbEntities.pas',
  Auth.func in '..\..\interfaces\Auth.func.pas',
  sng.types in '..\Allg\sng.types.pas';


begin
  {$IFDEF DEBUG}
  ReportMemoryLeaksOnShutdown := true;
  writeln('-- DEBUG ON --');
  {$ENDIF}
  try
    InitLogging( 'sngAuth', [ConsoleLogWriter], ParamStr(0));
    Assert( Log <> nil);
    Log.Info('create dmDatabase ...');
    dmDatabase := TdmDatabaseAuth.Create(nil);
    Log.Info('create dmServer ...');
    dmServer := TdmServerAuth.Create(nil);
    Sleep(1000);
    dmServer.StartServer;
    var cmd : string;
    cmd := '';
    repeat
      writeln('');
      writeln(' Programmende mit xxx <ENTER>: ');
      writeln('');
      readln(cmd);
    until cmd = 'xxx';
    dmServer.StopServer;
    dmServer.Free;
    dmDatabase.Free;
  except
    on E: Exception do
      begin
        Writeln(E.ClassName, ': ', E.Message);
        readln;
      end;
  end;
end.



dmServer



unit sng.dmServer;
// Basis Datenmodul für Kommunikation XData. Jeweilige Projekte davon ableiten.


interface


uses
  System.SysUtils, System.Classes, System.Rtti, Data.DB,


  Sparkle.HttpSys.Server, Sparkle.HttpServer.Context, Sparkle.HttpServer.Module,
  Sparkle.Middleware.Cors, Sparkle.Middleware.Compress, Sparkle.Middleware.Logging,




  XData.Server.Module, XData.Comp.ConnectionPool, XData.Comp.Server, XData.OpenAPI.Service,


  Aurelius.Drivers.Interfaces,


  sng.dmDatabase, Aurelius.Comp.Connection, Sparkle.Comp.JwtMiddleware, Sparkle.Comp.LoggingMiddleware, Sparkle.Comp.Server,
  Sparkle.Comp.CorsMiddleware, Sparkle.Comp.HttpSysDispatcher, Sparkle.Comp.GenericMiddleware;


type
  TdmServer = class(TDataModule)
    SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher;
    XDataServer: TXDataServer;
    XDataConnectionPool: TXDataConnectionPool;
    XDataServerCORS: TSparkleCorsMiddleware;
    XDataServerJWT: TSparkleJwtMiddleware;
    XDataServerGeneric: TSparkleGenericMiddleware;
    procedure DataModuleCreate(Sender: TObject);
    procedure DataModuleDestroy(Sender: TObject);
    procedure XDataServerGenericMiddlewareCreate(Sender: TObject; var Middleware: IHttpServerMiddleware);
  private
    function GetActive: boolean;
  public
    property Active : boolean read GetActive;
    procedure StartServer;
    procedure StopServer;
  end;


var
  dmServer: TdmServer;


implementation


{%CLASSGROUP 'System.Classes.TPersistent'}


{$R *.dfm}


uses
  sng.Server.Settings,
  sng.Utilities,
  sng.Logging;


procedure TdmServer.DataModuleCreate(Sender: TObject);
//var
//  Logging: TLoggingMiddleware;
begin
  XDataServer.BaseUrl := cBasisURL;
  XDataConnectionPool.Connection := dmDatabase.AureliusConnection;


  XDataServerJWT.Secret := constSophaKey32;


//  Logging := TLoggingMiddleware.Create( XDataLogger);
//  XDataServer.MiddlewareList.Add( Logging);


end;


procedure TdmServer.DataModuleDestroy(Sender: TObject);
begin
  StopServer;
end;


function TdmServer.GetActive: boolean;
begin
  result := SparkleHttpSysDispatcher.Active;
end;


procedure TdmServer.StartServer;
begin
  RegisterOpenApiService;
  SparkleHttpSysDispatcher.Start;
end;


procedure TdmServer.StopServer;
begin
  SparkleHttpSysDispatcher.Stop;
end;


procedure TdmServer.XDataServerGenericMiddlewareCreate(Sender: TObject; var Middleware: IHttpServerMiddleware);
begin
  Middleware := TLoggingMiddleware.Create( XDataLogger);
end;


end.


Logging


unit sng.Logging;


interface


uses
  System.SysUtils,
  System.Rtti,
  Bcl.Logging,
  Delphiprofi.FDK.Logging;


type
  TSetOfLogger = set of ( ConsoleLogWriter, FileLogWriter);


  TXDataLogger = class(TInterfacedObject, Bcl.Logging.ILogger)
    procedure Error(const AValue: TValue); overload;
    procedure Warning(const AValue: TValue);
    procedure Info(const AValue: TValue); overload;
    procedure Trace(const AValue: TValue);
    procedure Debug(const AValue: TValue); overload;
  end;


var
  GlobalConsoleLogger : IWriteLog;
  GlobalFileLogger    : IWriteLog;


  Log : ILog = nil;
  XDataLogger : TXDataLogger;


procedure InitLogging( AProgName : string; AStandardLogger : TSetOfLogger; AFilename : string = '');


implementation




Uses
  Delphiprofi.FDK.Logging.Consolelogger,
  Delphiprofi.FDK.Logging.FileLogger;


procedure InitLogging( AProgName : string; AStandardLogger : TSetOfLogger; AFilename : string = '');
begin
//  if (Log = nil) and (AStandardLogger <> []) then
    begin


      if ConsoleLogWriter in AStandardLogger then
        GlobalConsoleLogger := TLogFactory.AddQueuedWriter(TConsoleLogWriter.Create);


      if (FileLogWriter in AStandardLogger) and (AFilename <> '') then
        GlobalFileLogger := TLogFactory.AddQueuedWriter(TFileLogWriter.Create(AFilename, true));


      //Log := TLogFactory.GetLogger(NIL);
      Log.Debug( '');
      Log.Debug( AProgName + ': Start logging ...');


    end;
end;


{ TXDataLogger }


procedure TXDataLogger.Debug(const AValue: TValue);
begin
  Log.Debug( AValue.ToString);
end;


procedure TXDataLogger.Error(const AValue: TValue);
begin
  Log.Error( AValue.ToString);
end;


procedure TXDataLogger.Info(const AValue: TValue);
begin
  Log.Info( AValue.ToString);
end;


procedure TXDataLogger.Trace(const AValue: TValue);
begin
  Log.Verbose( AValue.ToString);
end;


procedure TXDataLogger.Warning(const AValue: TValue);
begin
  Log.Warn( AValue.ToString);
end;


initialization
  Log := TLogFactory.GetLogger(NIL);
  XDataLogger := TXDataLogger.Create;
finalization
  //XDataLogger.Free;
end.

You are mixing objects with reference-counted interfaces. 


TLoggingMiddleware expects an ILogger interface, when you pass XDataLogger to it, it gets the interface and it gets reference-counted. When dmServer is destroyed, TXDataServer is destroyed, including all its middleware classes, thus also the TLoggingMiddleware instance. The ILogger interface used by the logging middleware is then released, and the TXDataLogger instance is destroyed.

When the unit is finalized, you try to destroy XDataServer server again, and thus the reason you get an Access Violation.

Thank you. I will test it.
After so many years without reference-counted, I still have problems with the use of reference-counted.