Skip to content

Commit 94e9822

Browse files
authored
Adding custom formatting for logs (#1)
1 parent 2e1525a commit 94e9822

File tree

14 files changed

+288
-37
lines changed

14 files changed

+288
-37
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,6 @@ paket-files/
258258

259259
#VS Code
260260
.vscode
261+
262+
#Sonar Lint
263+
.sonarlint

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,68 @@ _logger.LogTrace("This won't be displayed, only critical will be");
174174
_logger.LogCritical("Critical message will be displayed");
175175
```
176176

177+
### Create your own formatting
178+
179+
You can use a custom formatter which will give you the name of the logger, the log level, the event ID, the message itself and a potential exception. The function definition should follow the following pattern:
180+
181+
```csharp
182+
public interface IMessageFormatter
183+
{
184+
string MessageFormatter(string className, LogLevel logLevel, EventId eventId, string state, Exception exception);
185+
}
186+
```
187+
188+
**Important**: this function will be called directly, without instantiating the class it is part of. So make sure either this function is a static, either it's part of the class using the logger. The static option always works. The interface is given for convenience and to give the format.
189+
190+
To setup the formatting, just use the following line. The type of the class containing the function and the exact name of the function are required.
191+
192+
```csharp
193+
LoggerExtensions.MessageFormatter = typeof(MyFormatter).GetType().GetMethod("MessageFormatterStatic");
194+
195+
public class MyFormatter
196+
{
197+
public string MessageFormatterStatic(string className, LogLevel logLevel, EventId eventId, string state, Exception exception)
198+
{
199+
string logstr = string.Empty;
200+
switch (logLevel)
201+
{
202+
case LogLevel.Trace:
203+
logstr = "TRACE: ";
204+
break;
205+
case LogLevel.Debug:
206+
logstr = "I love debug: ";
207+
break;
208+
case LogLevel.Warning:
209+
logstr = "WARNING: ";
210+
break;
211+
case LogLevel.Error:
212+
logstr = "ERROR: ";
213+
break;
214+
case LogLevel.Critical:
215+
logstr = "CRITICAL:";
216+
break;
217+
case LogLevel.None:
218+
case LogLevel.Information:
219+
default:
220+
break;
221+
}
222+
223+
string eventstr = eventId.Id != 0 ? $" Event ID: {eventId}, " : string.Empty;
224+
string msg = $"[{className}] {eventstr}{logstr} {state}";
225+
if (exception != null)
226+
{
227+
msg += $" {exception}";
228+
}
229+
230+
return msg;
231+
}
232+
}
233+
```
234+
235+
You are free to use anything you'd like and format as you like the message.
236+
237+
Note: It is **not** necessary to add a \r\n at the end, this is done by each logger.
238+
177239
## Code of Conduct
178240

179241
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using nanoFramework.TestFramework;
2+
using System;
3+
using System.Diagnostics;
4+
using nanoFramework.Logging;
5+
using Microsoft.Extensions.Logging;
6+
using nanoFramework.Logging.Debug;
7+
8+
namespace UnitTestDebugLogging
9+
{
10+
[TestClass]
11+
class FormattingTest
12+
{
13+
static DebugLogger _logger;
14+
15+
[Setup]
16+
public void SteupFormattingTest()
17+
{
18+
_logger = new DebugLogger(nameof(FormattingTest));
19+
LoggerExtensions.MessageFormatter = typeof(MyFormatter).GetType().GetMethod("MessageFormatterStatic");
20+
Debug.WriteLine($"{LoggerExtensions.MessageFormatter.Name}");
21+
_logger.MinLogLevel = LogLevel.Trace;
22+
}
23+
24+
[TestMethod]
25+
public void TestInvoke()
26+
{
27+
string msg = (string)LoggerExtensions.MessageFormatter.Invoke(null, new object[] { "test", LogLevel.Trace, new EventId(0), "some text", null });
28+
Debug.WriteLine(msg);
29+
}
30+
31+
[TestMethod]
32+
public void TestFormatting()
33+
{
34+
LogAll();
35+
}
36+
37+
[TestMethod]
38+
public void TestFormattingSimple()
39+
{
40+
LoggerExtensions.MessageFormatter = typeof(MyFormatter).GetType().GetMethod("MessageFormatterSimple");
41+
LogAll();
42+
}
43+
44+
private void LogAll()
45+
{
46+
Debug.WriteLine($"Expexcted level: {_logger.MinLogLevel}");
47+
_logger.LogTrace("{0} {1}", new object[] { "param 1", 42 });
48+
_logger.LogDebug("{0} {1}", new object[] { "param 1", 42 });
49+
_logger.LogInformation("Just some information and nothing else");
50+
_logger.LogWarning("{0} {1}", new object[] { "param 1", 42 });
51+
_logger.LogError(new Exception("Big problem"), "{0} {1}", new object[] { "param 1", 42 });
52+
_logger.LogCritical(42, new Exception("Insane problem"), "{0} {1}", new object[] { "param 1", 42 });
53+
}
54+
55+
[Cleanup]
56+
public void CleanupFormattingTest()
57+
{
58+
LoggerExtensions.MessageFormatter = null;
59+
}
60+
}
61+
62+
public class MyFormatter : IMessageFormatter
63+
{
64+
public string MessageFormatter(string className, LogLevel logLevel, EventId eventId, string state, Exception exception)
65+
=> MessageFormatterStatic(className, logLevel, eventId, state, exception);
66+
67+
public string MessageFormatterStatic(string className, LogLevel logLevel, EventId eventId, string state, Exception exception)
68+
{
69+
string logstr = string.Empty;
70+
switch (logLevel)
71+
{
72+
case LogLevel.Trace:
73+
logstr = "TRACE: ";
74+
break;
75+
case LogLevel.Debug:
76+
logstr = "I love debug: ";
77+
break;
78+
case LogLevel.Warning:
79+
logstr = "WARNING: ";
80+
break;
81+
case LogLevel.Error:
82+
logstr = "ERROR: ";
83+
break;
84+
case LogLevel.Critical:
85+
logstr = "CRITICAL:";
86+
break;
87+
case LogLevel.None:
88+
case LogLevel.Information:
89+
default:
90+
break;
91+
}
92+
93+
string eventstr = eventId.Id != 0 ? $" Event ID: {eventId}, " : string.Empty;
94+
string msg = $"[{className}] {eventstr}{logstr} {state}";
95+
if (exception != null)
96+
{
97+
msg += $" {exception}";
98+
}
99+
100+
return msg;
101+
}
102+
103+
public string MessageFormatterSimple(string className, LogLevel logLevel, EventId eventId, string state, Exception exception)
104+
{
105+
string msg = $"[{className}] {logLevel}-{state}";
106+
if (exception != null)
107+
{
108+
msg += $" {exception}";
109+
}
110+
111+
return msg;
112+
}
113+
}
114+
}

Tests/UnitTestDebugLogging/UnitTestDebugLogging.nfproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<ItemGroup>
3030
<Compile Include="DebugTest.cs" />
3131
<Compile Include="EventIdTests.cs" />
32+
<Compile Include="FormattingTest.cs" />
3233
<Compile Include="MemoryStreamTests.cs" />
3334
<Compile Include="MyTestComponent.cs" />
3435
<Compile Include="Properties\AssemblyInfo.cs" />

nanoFramework.Logging.Serial/SerialLogger.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
using Microsoft.Extensions.Logging;
77
using System;
8+
using System.Reflection;
89
using Windows.Devices.SerialCommunication;
910
using Windows.Storage.Streams;
1011

@@ -21,13 +22,20 @@ public class SerialLogger : ILogger
2122
/// Creates a new instance of the <see cref="SerialLogger"/>
2223
/// </summary>
2324
/// <param name="serialDevice">The serial port to use</param>
24-
public SerialLogger(ref SerialDevice serialDevice)
25+
/// <param name="loggerName">The logger name</param>
26+
public SerialLogger(ref SerialDevice serialDevice, string loggerName)
2527
{
2628
SerialDevice = serialDevice;
29+
LoggerName = loggerName;
2730
_outputDataWriter = new DataWriter(serialDevice.OutputStream);
2831
MinLogLevel = LogLevel.Debug;
2932
}
3033

34+
/// <summary>
35+
/// Name of the logger
36+
/// </summary>
37+
public string LoggerName { get; }
38+
3139
/// <summary>
3240
/// Name of the serial device
3341
/// </summary>
@@ -42,12 +50,21 @@ public SerialLogger(ref SerialDevice serialDevice)
4250
public bool IsEnabled(LogLevel logLevel) => logLevel >= MinLogLevel;
4351

4452
/// <inheritdoc />
45-
public void Log(LogLevel logLevel, EventId eventId, string state, Exception exception)
53+
public void Log(LogLevel logLevel, EventId eventId, string state, Exception exception, MethodInfo format)
4654
{
4755
if (logLevel >= MinLogLevel)
4856
{
49-
string msg = exception == null ? $"{state}\r\n" : $"{state} {exception}\r\n";
50-
_outputDataWriter.WriteString(msg);
57+
string msgSerial;
58+
if (format == null)
59+
{
60+
msgSerial = exception == null ? $"{state}\r\n" : $"{state} {exception}\r\n";
61+
}
62+
else
63+
{
64+
msgSerial = $"{(string)format.Invoke(null, new object[] { LoggerName, logLevel, eventId, state, exception })}\r\n";
65+
}
66+
67+
_outputDataWriter.WriteString(msgSerial);
5168
_outputDataWriter.Store();
5269
}
5370
}

nanoFramework.Logging.Serial/SerialLoggerFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public ILogger CreateLogger(string categoryName)
5555
_serial.StopBits = _stopBits;
5656
_serial.Handshake = _handshake;
5757
_serial.DataBits = _dataBits;
58-
return new SerialLogger(ref _serial);
58+
return new SerialLogger(ref _serial, categoryName);
5959
}
6060

6161
/// <inheritdoc />

nanoFramework.Logging.Stream/StreamLogger.cs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,35 @@
55
using Microsoft.Extensions.Logging;
66
using System;
77
using System.IO;
8+
using System.Reflection;
89
using System.Text;
910

1011
namespace nanoFramework.Logging.Stream
1112
{
1213
/// <summary>
1314
/// A logger that outputs to a <see cref="Stream"/>.
1415
/// </summary>
15-
public class StreamLogger : ILogger, IDisposable
16+
public class StreamLogger : ILogger
1617
{
17-
private System.IO.Stream _stream = null;
18+
private readonly System.IO.Stream _stream = null;
1819

1920
/// <summary>
2021
/// Creates a new instance of the <see cref="ILogger"/>
2122
/// </summary>
2223
/// <param name="stream">Stream to output the log to.</param>
23-
public StreamLogger(System.IO.Stream stream)
24+
/// <param name="loggerName">The logger name</param>
25+
public StreamLogger(System.IO.Stream stream, string loggerName)
2426
{
2527
_stream = stream;
28+
LoggerName = loggerName;
2629
MinLogLevel = LogLevel.Debug;
2730
}
2831

32+
/// <summary>
33+
/// Name of the logger
34+
/// </summary>
35+
public string LoggerName { get; }
36+
2937
/// <summary>
3038
/// Name of the logger
3139
/// </summary>
@@ -40,25 +48,24 @@ public StreamLogger(System.IO.Stream stream)
4048
public bool IsEnabled(LogLevel logLevel) => logLevel >= MinLogLevel;
4149

4250
/// <inheritdoc />
43-
public void Log(LogLevel logLevel, EventId eventId, string state, Exception exception)
51+
public void Log(LogLevel logLevel, EventId eventId, string state, Exception exception, MethodInfo format)
4452
{
4553
if (logLevel >= MinLogLevel)
4654
{
47-
string msg = exception == null ? $"{state}\r\n" : $"{state} {exception}\r\n";
48-
byte[] sampleBuffer = Encoding.UTF8.GetBytes(msg);
55+
string msgStream;
56+
if (format == null)
57+
{
58+
msgStream = exception == null ? $"{state}\r\n" : $"{state} {exception}\r\n";
59+
}
60+
else
61+
{
62+
msgStream = $"{(string)format.Invoke(null, new object[] { LoggerName, logLevel, eventId, state, exception })}\r\n";
63+
}
64+
65+
byte[] sampleBuffer = Encoding.UTF8.GetBytes(msgStream);
4966
_stream.Seek(0, SeekOrigin.End);
5067
_stream.Write(sampleBuffer, 0, sampleBuffer.Length);
5168
}
5269
}
53-
54-
/// <inheritdoc/>
55-
public void Dispose()
56-
{
57-
if(_stream != null)
58-
{
59-
_stream.Dispose();
60-
_stream = null;
61-
}
62-
}
6370
}
6471
}

nanoFramework.Logging.Stream/StreamLoggerFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public ILogger CreateLogger(string categoryName)
4040
throw new IOException();
4141
}
4242

43-
return new StreamLogger(_stream);
43+
return new StreamLogger(_stream, categoryName);
4444
}
4545

4646
/// <inheritdoc />

nanoFramework.Logging/DebugLogger.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
using Microsoft.Extensions.Logging;
77
using System;
8+
using System.Reflection;
89

910
namespace nanoFramework.Logging.Debug
1011
{
@@ -16,6 +17,7 @@ public class DebugLogger : ILogger
1617
/// <summary>
1718
/// Creates a new instance of the <see cref="DebugLogger"/>
1819
/// </summary>
20+
/// <param name="loggerName">The logger name</param>
1921
public DebugLogger(string loggerName)
2022
{
2123
LoggerName = loggerName;
@@ -36,11 +38,20 @@ public DebugLogger(string loggerName)
3638
public bool IsEnabled(LogLevel logLevel) => logLevel >= MinLogLevel;
3739

3840
/// <inheritdoc />
39-
public void Log(LogLevel logLevel, EventId eventId, string state, Exception exception)
41+
public void Log(LogLevel logLevel, EventId eventId, string state, Exception exception, MethodInfo format)
4042
{
4143
if (logLevel >= MinLogLevel)
4244
{
43-
string msg = exception == null ? state : $"{state} {exception}";
45+
string msg;
46+
if (format == null)
47+
{
48+
msg = exception == null ? state : $"{state} {exception}";
49+
}
50+
else
51+
{
52+
msg = (string)format.Invoke(null, new object[] { LoggerName, logLevel, eventId, state, exception });
53+
}
54+
4455
System.Diagnostics.Debug.WriteLine(msg);
4556
}
4657
}

0 commit comments

Comments
 (0)