Skip to content

Commit

Permalink
New Elastic Search appender
Browse files Browse the repository at this point in the history
  • Loading branch information
danieleteti committed Oct 8, 2018
1 parent fa44246 commit 847810d
Show file tree
Hide file tree
Showing 9 changed files with 952 additions and 28 deletions.
97 changes: 97 additions & 0 deletions LoggerPro.ElasticSearchAppender.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
unit LoggerPro.ElasticSearchAppender;

interface

uses
LoggerPro,
System.Net.HttpClientComponent,
System.SysUtils,
LoggerPro.RESTAppender,
System.Net.HttpClient;

type

{
Log appender for an ElasticSearch 6.4+ endpoint
Author: Daniele Teti (https://github.com/danieleteti/) and Salvatore Sparacino
}

TLoggerProElasticSearchAppender = class(TLoggerProRESTAppender)
private
fHTTP: THTTPClient;
public
constructor Create(aElasticHost: string; aElasticPort: integer;
aElasticIndex: string); reintroduce;
destructor Destroy; override;
procedure Setup; override;
procedure TearDown; override;
procedure TryToRestart(var Restarted: Boolean); override;
procedure WriteLog(const aLogItem: TLogItem); override;
end;

implementation

uses
System.Json,
System.DateUtils,
System.Classes;

{ TLoggerProElasticSearchAppender }

constructor TLoggerProElasticSearchAppender.Create(aElasticHost: string;
aElasticPort: integer; aElasticIndex: string);
var
lUrl: string;
begin
fHTTP := THTTPClient.Create;
lUrl := Format('%s:%d/%s/_doc', [aElasticHost, aElasticPort, aElasticIndex.ToLower]);
inherited Create(lUrl, 'application/json');
end;

destructor TLoggerProElasticSearchAppender.Destroy;
begin
fHTTP.Free;
inherited;
end;

procedure TLoggerProElasticSearchAppender.Setup;
begin
inherited;
end;

procedure TLoggerProElasticSearchAppender.TearDown;
begin
inherited;
end;

procedure TLoggerProElasticSearchAppender.TryToRestart(var Restarted: Boolean);
begin
inherited;
Restarted := True;
end;

procedure TLoggerProElasticSearchAppender.WriteLog(const aLogItem: TLogItem);
var
lRequestBody: TStringStream;
lJSON: TJSONObject;
begin
lJSON := TJSONObject.Create;
try
lJSON.AddPair('log_tag', TJSONString.Create(aLogItem.LogTag));
lJSON.AddPair('log_level', TJSONString.Create(aLogItem.LogTypeAsString));
lJSON.AddPair('log_message', TJSONString.Create(aLogItem.LogMessage));
lJSON.AddPair('log_datetime', TJSONString.Create(DateToISO8601(aLogItem.TimeStamp)));
lJSON.AddPair('timestamp', TJSONString.Create(DateToISO8601(aLogItem.TimeStamp)));

lRequestBody := TStringStream.Create(lJSON.ToString);
try
InternalWriteLog(GetRESTUrl, aLogItem, lRequestBody);
finally
lRequestBody.Free
end;
finally
lJSON.Free;
end;
end;

end.
71 changes: 44 additions & 27 deletions LoggerPro.RESTAppender.pas
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,37 @@ interface
var RetryCount: Integer);

TLoggerProRESTAppender = class(TLoggerProAppenderBase, ILogAppender)
private
strict private
FOnCreateData: TOnCreateData;
FOnNetSendError: TOnNetSendError;
fExtendedInfo: TLoggerProExtendedInfo;
fContentType: string;
procedure SetOnCreateData(const Value: TOnCreateData);
procedure SetOnNetSendError(const Value: TOnNetSendError);
protected
fRESTUrl: string;
fLastSignature: string;
fLogFormat: string;
fFormatSettings: TFormatSettings;
fExtendedInfoData: array [low(TLogExtendedInfo) .. high(TLogExtendedInfo)] of string;
procedure SetOnCreateData(const Value: TOnCreateData);
procedure SetOnNetSendError(const Value: TOnNetSendError);
strict protected
procedure LoadExtendedInfo;
function GetExtendedInfo: string;
public const
protected const
DEFAULT_LOG_FORMAT = '%0:s [TID %1:10u][%2:-8s] %3:s {EI%4:s}[%5:s]';
DEFAULT_EXTENDED_INFO = [TLogExtendedInfo.EIUserName, TLogExtendedInfo.EIComputerName, TLogExtendedInfo.EIProcessName,
TLogExtendedInfo.EIProcessID, TLogExtendedInfo.EIDeviceID];
DEFAULT_REST_URL = 'http://127.0.0.1:8080/api/logs';

procedure InternalWriteLog(const aURI: string; const aLogItem: TLogItem; const aStream: TStream);
public
function GetRESTUrl: string;
procedure SetRESTUrl(const Value: string);
procedure WriteLog(const aLogItem: TLogItem); override;
constructor Create(aRESTUrl: string = DEFAULT_REST_URL; aContentType: string = 'text/plain';
aLogExtendedInfo: TLoggerProExtendedInfo = DEFAULT_EXTENDED_INFO; aLogFormat: string = DEFAULT_LOG_FORMAT); reintroduce;
property RESTUrl: string read GetRESTUrl write SetRESTUrl;
property OnCreateData: TOnCreateData read FOnCreateData write SetOnCreateData;
property OnNetSendError: TOnNetSendError read FOnNetSendError write SetOnNetSendError;
procedure TearDown; override;
procedure Setup; override;
procedure WriteLog(const aLogItem: TLogItem); override;
function CreateData(const SrcLogItem: TLogItem): TStream; virtual;
function FormatLog(const aLogItem: TLogItem): string; virtual;
end;
Expand All @@ -79,6 +79,7 @@ implementation

{$IFDEF MSWINDOWS }


function GetUserFromWindows: string;
var
iLen: Cardinal;
Expand All @@ -101,6 +102,7 @@ function GetComputerNameFromWindows: string;

{$ENDIF}


constructor TLoggerProRESTAppender.Create(aRESTUrl: string = DEFAULT_REST_URL; aContentType: string = 'text/plain';
aLogExtendedInfo: TLoggerProExtendedInfo = DEFAULT_EXTENDED_INFO; aLogFormat: string = DEFAULT_LOG_FORMAT);
begin
Expand All @@ -114,7 +116,7 @@ constructor TLoggerProRESTAppender.Create(aRESTUrl: string = DEFAULT_REST_URL; a

function TLoggerProRESTAppender.CreateData(const SrcLogItem: TLogItem): TStream;
var
lLog: String;
lLog: string;
begin
Result := nil;
try
Expand Down Expand Up @@ -236,38 +238,38 @@ procedure TLoggerProRESTAppender.TearDown;
inherited;
end;

procedure TLoggerProRESTAppender.WriteLog(const aLogItem: TLogItem);
procedure TLoggerProRESTAppender.InternalWriteLog(const aURI: string; const aLogItem: TLogItem; const aStream: TStream);
var
lHTTPCli: THTTPClient;
lURI: string;
lData: TStream;
lRetryCount: Integer;
lResp: IHTTPResponse;
begin
lRetryCount := 0;
lHTTPCli := THTTPClient.Create;
try
lURI := RESTUrl + '/' + TNetEncoding.URL.Encode(aLogItem.LogTag.Trim) + '/' + TNetEncoding.URL.Encode(aLogItem.LogTypeAsString);
lData := CreateData(aLogItem);
if Assigned(lData) then
if Assigned(aStream) then
begin
repeat
try
// Set very short timeouts: this is a local call and we don't want to block the queue for too long.
lHTTPCli.ConnectionTimeout := 100;
lHTTPCli.ResponseTimeout := 200;
lData.Seek(0, soFromBeginning);
lHTTPCli.Post(lURI, lData, nil, [TNetHeader.Create('content-type', fContentType)]);
break;
aStream.Seek(0, soFromBeginning);
lResp := lHTTPCli.Post(aURI, aStream, nil, [TNetHeader.Create('content-type', fContentType)]);
if not(lResp.StatusCode in [200, 201]) then
begin
raise ELoggerPro.Create(lResp.ContentAsString);
end;
except
// on E: ENetHTTPClientException do
// begin
// // if there is an event handler for net exception, call it
// if Assigned(FOnNetSendError) then
// OnNetSendError(Self, aLogItem, E, lRetryCount);
// // if the handler has set FRetryCount to a positive value then retry the call
// if lRetryCount <= 0 then
// break;
// end;
// on E: ENetHTTPClientException do
// begin
// // if there is an event handler for net exception, call it
// if Assigned(FOnNetSendError) then
// OnNetSendError(Self, aLogItem, E, lRetryCount);
// // if the handler has set FRetryCount to a positive value then retry the call
// if lRetryCount <= 0 then
// break;
// end;
on E: Exception do
begin
// if there is an event handler for net exception, call it
Expand All @@ -286,4 +288,19 @@ procedure TLoggerProRESTAppender.WriteLog(const aLogItem: TLogItem);
end;
end;

procedure TLoggerProRESTAppender.WriteLog(const aLogItem: TLogItem);
var
lURI: string;
lData: TStream;
begin
lURI := RESTUrl + '/' + TNetEncoding.URL.Encode(aLogItem.LogTag.Trim) + '/' + TNetEncoding.URL.Encode(aLogItem.LogTypeAsString);
lData := CreateData(aLogItem);
try
if Assigned(lData) then
InternalWriteLog(lURI, aLogItem, lData);
finally
lData.Free;
end;
end;

end.
2 changes: 1 addition & 1 deletion LoggerPro.pas
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ function GetDefaultFormatSettings: TFormatSettings;
begin
Result.DateSeparator := '-';
Result.TimeSeparator := ':';
Result.ShortDateFormat := 'YYY-MM-DD HH:NN:SS:ZZZ';
Result.ShortDateFormat := 'YYYY-MM-DD HH:NN:SS:ZZZ';
Result.ShortTimeFormat := 'HH:NN:SS';
end;

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ LoggerPro is compatibile with
- New REST appender with support for extended information (samples for Windows and Android)
- Extended information are supported in Windows (fully) and Android (partially)
- In the sample folder is provided also the `RESTLogCollector`
- New [Elastic Search](https://www.elastic.co/products/elasticsearch) Log appender (Thanks to Salvatore Sparacino)


## Getting started
Expand Down
62 changes: 62 additions & 0 deletions samples/120_elastic_search_appender/ESAppenderFormU.dfm
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
object MainForm: TMainForm
Left = 0
Top = 0
Caption = 'LoggerPro SAMPLE'
ClientHeight = 142
ClientWidth = 584
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Visible = True
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 8
Top = 8
Width = 137
Height = 57
Caption = 'DEBUG'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 151
Top = 8
Width = 137
Height = 57
Caption = 'INFO'
TabOrder = 1
OnClick = Button2Click
end
object Button3: TButton
Left = 294
Top = 8
Width = 137
Height = 57
Caption = 'WARNING'
TabOrder = 2
OnClick = Button3Click
end
object Button4: TButton
Left = 437
Top = 8
Width = 137
Height = 57
Caption = 'ERROR'
TabOrder = 3
OnClick = Button4Click
end
object Button5: TButton
Left = 8
Top = 71
Width = 280
Height = 57
Caption = 'Multithread logging'
TabOrder = 4
OnClick = Button5Click
end
end
Loading

0 comments on commit 847810d

Please sign in to comment.