Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed newlines in log message being stripped, and multiple log entries on the same connection #2

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/src/Tests/TestSerilog/bin
/src/Tests/TestSerilog/obj
/src/Log2Console/bin
/src/.vs/LogFmwk/v14/*.suo
/src/External/WindowsAPICodePack/Core/bin
/src/External/WindowsAPICodePack/Core/obj
/src/External/WindowsAPICodePack/Shell/obj
/src/External/WindowsAPICodePack/Shell/bin
/src/ICSharpCode.TextEditor/obj
/src/Log2Console/obj
/src/RichTextBoxLinks/bin
/src/RichTextBoxLinks/obj
/src/Tests/Test/obj
/src/Tests/Test/bin
/src/Tests/TestNLog/obj
/src/Tests/TestNLog/bin
/src/Tests/TestOther/bin
/src/Tests/TestOther/obj
/src/Log2Console/*.log
/src/packages
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Log2Console
GitHub clone of SVN repo https://log2console.svn.codeplex.com/svn (cloned by http://svn2github.com/)

It appeared to me that the project on Codeplex was no longer being maintained, so I forked to project to make some fixes that were bothering me.

Changes from Codeplex version
--
* TCP Receiver will attempt to parse multiple messages received over the same socket connection.
* UDP Reciever, added support for Serilog messages in addition to Log4J/NLog.
15 changes: 15 additions & 0 deletions license.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Copied from https://log2console.codeplex.com/license

New BSD License (BSD)
Copyright (c) 2007, Rémy Baudet
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of Log2Console nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
9 changes: 9 additions & 0 deletions src/Log2Console/Log/LogMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public class LogMessage
/// </summary>
public uint SourceFileLineNr;

/// <summary>
/// The log as it was read.
/// </summary>
public string RawLog;

public void CheckNull()
{
if (string.IsNullOrEmpty(LoggerName))
Expand Down Expand Up @@ -167,8 +172,12 @@ public string GetMessageDetails()
sb.Append(@"\b " + fieldType.Field + @": \b0 ");
if (info.Length > 40)
sb.Append(@" \line ");
info = info.Replace("\r\n", @" \line ");
info = info.Replace("\n", @" \line ");
sb.Append(info + @" \line ");
}
// Maybe this isn't a good idea, possibly on a new tab.
sb.Append(@"\line \line Raw Log \line " + this.RawLog);
sb.Append(@"}");
return sb.ToString();
}
Expand Down
13 changes: 9 additions & 4 deletions src/Log2Console/Log2Console.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="ICSharpCode.TextEditor, Version=3.2.1.6466, Culture=neutral, PublicKeyToken=4d61825e8dd49f1a, processorArchitecture=MSIL">
<HintPath>..\packages\ICSharpCode.TextEditor.3.2.1.6466\lib\Net20\ICSharpCode.TextEditor.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\External\log4net.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Design" />
Expand Down Expand Up @@ -121,6 +121,7 @@
<Compile Include="MainForm.Designer.cs">
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="Receiver\SerilogParser.cs" />
<Compile Include="Utils.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down Expand Up @@ -274,6 +275,10 @@
<Folder Include="Configuration\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.TextEditor\ICSharpCode.TextEditor.csproj">
<Project>{2d18be89-d210-49eb-a9dd-2246fbb3df6d}</Project>
<Name>ICSharpCode.TextEditor</Name>
</ProjectReference>
<ProjectReference Include="..\RichTextBoxLinks\RichTextBoxLinks.csproj">
<Project>{8B833B00-BB4C-4FAF-B6C4-BF77824F96A1}</Project>
<Name>RichTextBoxLinks</Name>
Expand Down
4 changes: 2 additions & 2 deletions src/Log2Console/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.6.0.2")]
[assembly: AssemblyFileVersion("1.6.0.2")]
[assembly: AssemblyVersion("1.6.*")]
//[assembly: AssemblyFileVersion("1.6.*")]
95 changes: 93 additions & 2 deletions src/Log2Console/Receiver/ReceiverUtils.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Log2Console.Log;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

namespace Log2Console.Receiver
Expand All @@ -24,7 +26,7 @@ public static string GetTypeDescription(Type type)

static XmlReaderSettings CreateSettings()
{
return new XmlReaderSettings { CloseInput = false, ValidationType = ValidationType.None };
return new XmlReaderSettings { CloseInput = false, ValidationType = ValidationType.None, ConformanceLevel = ConformanceLevel.Fragment };
}

/// <summary>
Expand Down Expand Up @@ -53,6 +55,92 @@ public static LogMessage ParseLog4JXmlLogEvent(Stream logStream, string defaultL
return ParseLog4JXmlLogEvent(reader, defaultLogger);
}

/// <summary>
/// Try to parse the xml.
/// </summary>
/// <param name="outerXml"></param>
/// <param name="defaultLogger"></param>
/// <returns></returns>
private static LogMessage TryParseLog4JXmlLogEvent(string outerXml, string defaultLogger)
{
LogMessage logMessage;
try
{
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(outerXml));
logMessage = ParseLog4JXmlLogEvent(ms, defaultLogger);
}
catch(Exception e)
{
logMessage = new LogMessage()
{
LoggerName = nameof(Log2Console.Receiver.ReceiverUtils),
RootLoggerName = nameof(Log2Console.Receiver.ReceiverUtils),
ThreadName = "NA",
Message = "Error parsing log" + Environment.NewLine + outerXml,
TimeStamp = DateTime.Now,
Level = LogLevels.Instance[LogLevel.Warn],
ExceptionString = e.Message
};
}
logMessage.RawLog = outerXml;
return logMessage;
}

/// <summary>
/// IEnumerable of log events
/// </summary>
/// <param name="logStream"></param>
/// <param name="defaultLogger"></param>
/// <returns></returns>
public static IEnumerable<LogMessage> ParseLog4JXmlLogEvents(Stream logStream, string defaultLogger)
{
var buffer = new byte[4096];
int bytesRead = 0;
int startPos = 0;
while((bytesRead = logStream.Read(buffer, startPos, buffer.Length - startPos)) > 0)
{
string xmlText = Encoding.UTF8.GetString(buffer, 0, bytesRead + startPos);

int leftOversPos = 0;
// This regex will match the start and end tags we are looking for.
var matches = Regex.Matches(xmlText, $"<(/?)\\s*(log4j:event)[^<>]*(/?)>");

// Break up the log messages into single log messages before processing.
foreach (Match match in matches)
{
bool IsBeginElement = String.IsNullOrWhiteSpace(match.Groups[1].Value);
bool IsEmptyElement = match.Value.EndsWith("/>");
if (IsBeginElement)
{
// Some data before the start tag, this will probably fail.
if (startPos < match.Index)
{
yield return TryParseLog4JXmlLogEvent(xmlText.Substring(startPos, match.Index - startPos), defaultLogger);
}
// Empty XML Element, this will always fail, but go ahead and try anyway.
if (IsEmptyElement)
{
yield return TryParseLog4JXmlLogEvent(xmlText.Substring(match.Index, match.Length), defaultLogger);
leftOversPos = startPos = match.Index + match.Length;
}
else
{
startPos = match.Index;
}
}
else // End element process outer xml
{
yield return TryParseLog4JXmlLogEvent(xmlText.Substring(startPos, match.Index + match.Length - startPos), defaultLogger);
leftOversPos = startPos = match.Index + match.Length;
}
}

var leftOvers = Encoding.UTF8.GetBytes(xmlText.Substring(leftOversPos));
leftOvers.CopyTo(buffer, 0);
startPos = leftOvers.Length;
}
}

/// <summary>
/// Parse LOG4JXml from string
/// </summary>
Expand Down Expand Up @@ -99,7 +187,10 @@ public static LogMessage ParseLog4JXmlLogEvent(XmlReader reader, string defaultL
{
var logMsg = new LogMessage();

reader.Read();
while (!reader.EOF && (reader.NodeType != XmlNodeType.Element || reader.Name != "log4j:event"))
{
reader.Read();
}
if ((reader.MoveToContent() != XmlNodeType.Element) || (reader.Name != "log4j:event"))
throw new Exception("The Log Event is not a valid log4j Xml block.");

Expand Down
70 changes: 70 additions & 0 deletions src/Log2Console/Receiver/SerilogParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Log2Console.Log;
using Newtonsoft.Json.Linq;

namespace Log2Console.Receiver
{
class SerilogParser
{
internal static LogMessage Parse(string logEvent, string defaultLogger)
{
LogMessage message = null;
try
{
message = ParseEvent(logEvent);
}
catch (Exception ex)
{
message = new LogMessage
{
LoggerName = defaultLogger,
RootLoggerName = defaultLogger,
ThreadName = "N/A",
Message = logEvent,
TimeStamp = DateTime.Now,
Level = LogLevels.Instance[LogLevel.Info],
ExceptionString = ex.Message
};
}
return message;
}

internal static LogMessage ParseEvent(string logEvent)
{
var logJson = JObject.Parse(logEvent);
LogMessage logMessage = new LogMessage();
foreach (var child in logJson.Children().OfType<JProperty>())
{
#if DEBUG
Debug.WriteLine($"{child.Name}={child.Value}");
#endif
switch (child.Name)
{
case "timestamp":
logMessage.TimeStamp = DateTime.Parse(child.Value.ToString());
break;
case "level":
var levels = new[] {"Verbose", "Debug", "Information", "Warning", "Error", "Fatal"};
logMessage.Level = LogLevels.Instance[Array.IndexOf(levels, child.Value.ToString())];
break;
case "message":
logMessage.Message = child.Value.ToString();
break;
case "sourceContext":
logMessage.LoggerName = child.Value.ToString();
break;
default:
logMessage.Message += $"{Environment.NewLine}{child.Name}:{child.Value}";
logMessage.Properties[child.Name] = child.Value.ToString();
break;
}
}
return logMessage;
}
}

}
19 changes: 12 additions & 7 deletions src/Log2Console/Receiver/TcpReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,26 @@ void Start(object newSocket)
using (var ns = new NetworkStream(socket, FileAccess.Read, false))
while (_socket != null)
{
var logMsg = ReceiverUtils.ParseLog4JXmlLogEvent(ns, "TcpLogger");
logMsg.RootLoggerName = logMsg.LoggerName;
logMsg.LoggerName = string.Format(":{1}.{0}", logMsg.LoggerName, _port);

if (Notifiable != null)
Notifiable.Notify(logMsg);
var logMessages = ReceiverUtils.ParseLog4JXmlLogEvents(ns, "TcpLogger");
foreach (var logMessage in logMessages)
{
logMessage.RootLoggerName = logMessage.LoggerName;
logMessage.LoggerName = string.Format(":{1}.{0}", logMessage.LoggerName, _port);

if (Notifiable != null)
Notifiable.Notify(logMessage);
}
}
}
catch (IOException)
catch (IOException e)
{
Console.WriteLine(e);
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.WriteLine("Connection closed");
}

public override void Terminate()
Expand Down
Loading