diff --git a/Packages/DXE2/X2LogJsonDXE2.dpk b/Packages/DXE2/X2LogJsonDXE2.dpk
index 231d32c..aa92f19 100644
--- a/Packages/DXE2/X2LogJsonDXE2.dpk
+++ b/Packages/DXE2/X2LogJsonDXE2.dpk
@@ -30,7 +30,8 @@ package X2LogJsonDXE2;
requires
rtl,
- X2LogDXE2;
+ X2LogDXE2,
+ soaprtl;
contains
X2Log.TextFormatter.Json in '..\..\X2Log.TextFormatter.Json.pas',
diff --git a/Packages/DXE2/X2LogJsonDXE2.dproj b/Packages/DXE2/X2LogJsonDXE2.dproj
index 63fae2e..837cd8d 100644
--- a/Packages/DXE2/X2LogJsonDXE2.dproj
+++ b/Packages/DXE2/X2LogJsonDXE2.dproj
@@ -125,6 +125,7 @@
+
diff --git a/X2Log.Observer.LogFile.pas b/X2Log.Observer.LogFile.pas
index ff9faeb..4187c32 100644
--- a/X2Log.Observer.LogFile.pas
+++ b/X2Log.Observer.LogFile.pas
@@ -69,6 +69,7 @@ type
protected
{ IX2LogTextFormatterHelper }
function GetDetailsFilename: string;
+ function SaveDetailsToStream(AStream: TStream): Boolean;
property Entry: TX2LogQueueEntry read FEntry;
property LogFileName: string read FLogFileName;
@@ -246,4 +247,15 @@ begin
end;
end;
+
+function TX2LogFileTextFormatterHelper.SaveDetailsToStream(AStream: TStream): Boolean;
+var
+ logDetailsStreamable: IX2LogDetailsStreamable;
+
+begin
+ Result := Supports(Entry.Details, IX2LogDetailsStreamable, logDetailsStreamable);
+ if Result then
+ logDetailsStreamable.SaveToStream(AStream);
+end;
+
end.
diff --git a/X2Log.TextFormatter.Intf.pas b/X2Log.TextFormatter.Intf.pas
index 020c275..5c1db64 100644
--- a/X2Log.TextFormatter.Intf.pas
+++ b/X2Log.TextFormatter.Intf.pas
@@ -2,12 +2,15 @@ unit X2Log.TextFormatter.Intf;
interface
uses
+ System.Classes,
+
X2Log.Intf;
type
IX2LogTextFormatterHelper = interface
['{D1A1DAD5-0F96-491F-8BD5-0B9D0BE87C32}']
function GetDetailsFilename: string;
+ function SaveDetailsToStream(AStream: TStream): Boolean;
end;
IX2LogTextFormatter = interface
diff --git a/X2Log.TextFormatter.Json.pas b/X2Log.TextFormatter.Json.pas
index df71314..61f192a 100644
--- a/X2Log.TextFormatter.Json.pas
+++ b/X2Log.TextFormatter.Json.pas
@@ -22,29 +22,45 @@ type
TX2LogJsonTextFormatter = class(TInterfacedObject, IX2LogTextFormatter)
private
FSingleLine: Boolean;
+ FInlineFields: Boolean;
+ FInlineDetails: Boolean;
FStyle: TX2LogJsonStyle;
protected
function GetText(AHelper: IX2LogTextFormatterHelper; ALevel: TX2LogLevel; ADateTime: TDateTime; const AMessage: string; const ACategory: string; ADetails: IX2LogDetails): string;
procedure AddDictionaryDetails(AObject: TJsonObject; ADetails: IX2LogDetailsDictionary);
property SingleLine: Boolean read FSingleLine;
+ property InlineFields: Boolean read FInlineFields;
+ property InlineDetails: Boolean read FInlineDetails;
property Style: TX2LogJsonStyle read FStyle;
public
- constructor Create(ASingleLine: Boolean = True; AStyle: TX2LogJsonStyle = TX2LogJsonStyle.Default);
+ {
+ If AInlineFields is False, dictionary keys/values will be added to a
+ "fields" object instead of directly in the message, similar to Serilog's
+ Elasticsearch sink option with the same name.
+
+ If AInlineDetails is False, the details will be written to a separate file and
+ the detailsFile value will contain the file name.
+ }
+ constructor Create(ASingleLine: Boolean = True; AStyle: TX2LogJsonStyle = TX2LogJsonStyle.Default; AInlineFields: Boolean = True; AInlineDetails: Boolean = True);
end;
implementation
uses
+ Soap.EncdDecd,
+ System.Classes,
System.SysUtils;
{ TX2LogJsonTextFormatter }
-constructor TX2LogJsonTextFormatter.Create(ASingleLine: Boolean; AStyle: TX2LogJsonStyle);
+constructor TX2LogJsonTextFormatter.Create(ASingleLine: Boolean; AStyle: TX2LogJsonStyle; AInlineFields, AInlineDetails: Boolean);
begin
inherited Create;
FSingleLine := ASingleLine;
+ FInlineFields := AInlineFields;
+ FInlineDetails := AInlineDetails;
FStyle := AStyle;
end;
@@ -58,6 +74,8 @@ var
line: TJsonObject;
dictionaryDetails: IX2LogDetailsDictionary;
detailsFileName: string;
+ detailsStream: TMemoryStream;
+ encodedStream: TStringStream;
begin
line := TJsonObject.Create;
@@ -82,12 +100,36 @@ begin
if Supports(ADetails, IX2LogDetailsDictionary, dictionaryDetails) then
begin
- AddDictionaryDetails(line, dictionaryDetails);
+ if InlineFields then
+ AddDictionaryDetails(line, dictionaryDetails)
+ else
+ AddDictionaryDetails(line.O['fields'], dictionaryDetails);
end else
begin
- detailsFileName := AHelper.GetDetailsFilename;
- if Length(detailsFileName) > 0 then
- line.S['details'] := ExtractFileName(detailsFileName);
+ if InlineDetails then
+ begin
+ detailsStream := TMemoryStream.Create;
+ try
+ if AHelper.SaveDetailsToStream(detailsStream) then
+ begin
+ detailsStream.Position := 0;
+ encodedStream := TStringStream.Create;
+ try
+ EncodeStream(detailsStream, encodedStream);
+ line.S['details'] := encodedStream.DataString;
+ finally
+ FreeAndNil(encodedStream);
+ end;
+ end;
+ finally
+ FreeAndNil(detailsStream);
+ end;
+ end else
+ begin
+ detailsFileName := AHelper.GetDetailsFilename;
+ if Length(detailsFileName) > 0 then
+ line.S['detailsFile'] := ExtractFileName(detailsFileName);
+ end;
end;
Result := line.ToJSON(SingleLine);