diff --git a/README.md b/README.md
index d17d13e..feaa0cb 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,8 @@
| nanoFramework.Logging.Serial (preview) | [](https://dev.azure.com/nanoframework/nanoframework.Logging/_build/latest?definitionId=71&branchName=develop) | [](https://www.nuget.org/packages/nanoFramework.Logging.Serial/) |
| nanoFramework.Logging.Stream | [](https://dev.azure.com/nanoframework/nanoframework.Logging/_build/latest?definitionId=71&branchName=main) | [](https://www.nuget.org/packages/nanoFramework.Logging.Stream/) |
| nanoFramework.Logging.Stream (preview) | [](https://dev.azure.com/nanoframework/nanoframework.Logging/_build/latest?definitionId=71&branchName=develop) | [](https://www.nuget.org/packages/nanoFramework.Logging.Stream/) |
+| nanoFramework.Logging.Syslog | [](https://dev.azure.com/nanoframework/nanoframework.Logging/_build/latest?definitionId=71&branchName=main) | [](https://www.nuget.org/packages/nanoFramework.Logging.Syslog/) |
+| nanoFramework.Logging.Syslog (preview) | [](https://dev.azure.com/nanoframework/nanoframework.Logging/_build/latest?definitionId=71&branchName=develop) | [](https://www.nuget.org/packages/nanoFramework.Logging.Syslog/) |
## Feedback and documentation
diff --git a/nanoFramework.Logging.Syslog.nuspec b/nanoFramework.Logging.Syslog.nuspec
new file mode 100644
index 0000000..79c3bcc
--- /dev/null
+++ b/nanoFramework.Logging.Syslog.nuspec
@@ -0,0 +1,42 @@
+
+
+
+ nanoFramework.Logging.Syslog
+ $version$
+ nanoFramework.Logging.Syslog
+ nanoFramework project contributors
+ nanoFramework,dotnetfoundation
+ false
+ LICENSE.md
+
+
+ docs\README.md
+ false
+ https://github.com/nanoframework/nanoFramework.Logging
+ images\nf-logo.png
+
+ Copyright (c) .NET Foundation and Contributors
+ This package includes the nanoFramework.Logging.Syslog assembly (Syslog Logging only) for .NET nanoFramework C# projects.
+There is also a package with the Serial Logging, another with Stream Logging only and another with the basic Debug Logging.
+ nanoFramework C# csharp netmf netnf nanoFramework.Logging.Syslog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nanoFramework.Logging.Syslog/Facility.cs b/nanoFramework.Logging.Syslog/Facility.cs
new file mode 100644
index 0000000..6bacae4
--- /dev/null
+++ b/nanoFramework.Logging.Syslog/Facility.cs
@@ -0,0 +1,110 @@
+//
+// Copyright (c) .NET Foundation and Contributors
+// See LICENSE file in the project root for full license information.
+//
+
+namespace nanoFramework.Logging.Syslog
+{
+ ///
+ /// Available facility types (RFC 3164).
+ ///
+ public enum Facility
+ {
+ ///
+ /// kernel messages
+ ///
+ Kern = 0,
+ ///
+ /// user-level messages
+ ///
+ User = 1,
+ ///
+ /// mail system
+ ///
+ Mail = 2,
+ ///
+ /// system daemons
+ ///
+ Daemon = 3,
+ ///
+ /// security/authorization messages
+ ///
+ Auth = 4,
+ ///
+ /// messages generated internally by syslogd
+ ///
+ Syslog = 5,
+ ///
+ /// line printer subsystem
+ ///
+ LPR = 6,
+ ///
+ /// network news subsystem
+ ///
+ News = 7,
+ ///
+ /// UUCP subsystem
+ ///
+ UUCP = 8,
+ ///
+ /// clock daemon
+ ///
+ Cron = 9,
+ ///
+ /// security/authorization messages
+ ///
+ AuthPriv = 10,
+ ///
+ /// FTP daemon
+ ///
+ FTP = 11,
+ ///
+ /// NTP subsystem
+ ///
+ NTP = 12,
+ ///
+ /// log audit
+ ///
+ Audit = 13,
+ ///
+ /// log alert
+ ///
+ Audit2 = 14,
+ ///
+ /// clock daemon
+ ///
+ Cron2 = 15,
+ ///
+ /// local use 0 (local0)
+ ///
+ Local0 = 16,
+ ///
+ /// local use 1 (local1)
+ ///
+ Local1 = 17,
+ ///
+ /// local use 2 (local2)
+ ///
+ Local2 = 18,
+ ///
+ /// local use 3 (local3)
+ ///
+ Local3 = 19,
+ ///
+ /// local use 4 (local4)
+ ///
+ Local4 = 20,
+ ///
+ /// local use 5 (local5)
+ ///
+ Local5 = 21,
+ ///
+ /// local use 6 (local6)
+ ///
+ Local6 = 22,
+ ///
+ /// local use 7 (local7)
+ ///
+ Local7 = 23
+ }
+}
diff --git a/nanoFramework.Logging.Syslog/Properties/AssemblyInfo.cs b/nanoFramework.Logging.Syslog/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..387680d
--- /dev/null
+++ b/nanoFramework.Logging.Syslog/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("nanoFramework.Logging")]
+[assembly: AssemblyCompany("nanoFramework Contributors")]
+[assembly: AssemblyProduct("nanoFramework.Logging")]
+[assembly: AssemblyCopyright("Copyright (c) .NET Foundation and Contributors")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+/////////////////////////////////////////////////////////////////
+// This attribute is mandatory when building Interop libraries //
+// update this whenever the native assembly signature changes //
+[assembly: AssemblyNativeVersion("1.0.0.0")]
+/////////////////////////////////////////////////////////////////
diff --git a/nanoFramework.Logging.Syslog/Severity.cs b/nanoFramework.Logging.Syslog/Severity.cs
new file mode 100644
index 0000000..4c715ce
--- /dev/null
+++ b/nanoFramework.Logging.Syslog/Severity.cs
@@ -0,0 +1,52 @@
+//
+// Copyright (c) .NET Foundation and Contributors
+// See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.Extensions.Logging;
+
+namespace nanoFramework.Logging.Syslog
+{
+ ///
+ /// Severity represents the Severity in the Syslog messages. This corresponds to the values defined in RFC 3164 except None which means that the message won't be sent.
+ ///
+ public enum Severity
+ {
+ ///
+ /// Not an official RFC3164 facility. No entry will be generated when called with this Severity
+ ///
+ None = -1,
+ ///
+ /// Emergency: system is unusable. No equivalent in
+ ///
+ Emergency,
+ ///
+ /// Alert: action must be taken immediately. No equivalent in
+ ///
+ Alert,
+ ///
+ /// Critical: critical conditions. Mapped to
+ ///
+ Critical,
+ ///
+ /// Error: error conditions. Mapped to
+ ///
+ Error,
+ ///
+ /// Warning: warning conditions. Mapped to
+ ///
+ Warning,
+ ///
+ /// Notice: normal but significant condition. No equivalent in
+ ///
+ Notice,
+ ///
+ /// Informational: informational messages. Mapped to
+ ///
+ Informational,
+ ///
+ /// Debug: debug-level messages. Mapped to and
+ ///
+ Debug
+ };
+}
diff --git a/nanoFramework.Logging.Syslog/SyslogClient.cs b/nanoFramework.Logging.Syslog/SyslogClient.cs
new file mode 100644
index 0000000..1e68f2b
--- /dev/null
+++ b/nanoFramework.Logging.Syslog/SyslogClient.cs
@@ -0,0 +1,179 @@
+//
+// Copyright (c) .NET Foundation and Contributors
+// See LICENSE file in the project root for full license information.
+//
+
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+namespace nanoFramework.Logging.Syslog
+{
+ ///
+ /// Syslog Client that sends logs to a Syslog server following RFC3164 format.
+ ///
+ public class SyslogClient : IDisposable
+ {
+ private readonly Socket _socket;
+ private bool _disposed;
+
+ ///
+ /// Default Syslog UDP port : 514
+ ///
+ public const int DefaultPort = 514;
+
+ ///
+ /// Create a instance that will send RFC3164 compliant messages to a Syslog server.
+ ///
+ /// Endpoint of the server.
+ /// 'Hostname' part of the RFC3164 message.
+ /// used by this logger.
+ /// Local IP address to bind (if null will be used).
+ /// Local port to bind to (0 to choose available port).
+ public SyslogClient(
+ IPEndPoint endpoint,
+ string localHostname,
+ Facility facility = default,
+ IPAddress localAddress = null,
+ int localPort = 0)
+ {
+ Facility = facility;
+ LocalHostname = localHostname ?? throw new ArgumentNullException(nameof(localHostname));
+
+ _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ _socket.Bind(new IPEndPoint(localAddress ?? IPAddress.Any, localPort));
+ _socket.Connect(endpoint);
+ }
+
+ ///
+ /// Create a instance that will send RFC3164 compliant messages to a Syslog server.
+ ///
+ /// Fully Qualified Domain Name of the Syslog server.
+ /// Port of the Syslog server.
+ /// 'Hostname' part of the RFC3164 message (if null, 'nanoframework' will be used).
+ /// used by this logger.
+ /// Local IP address to bind to (if null will be used).
+ /// Local port to bind to (0 to choose available port).
+ public SyslogClient(
+ string hostname,
+ int port,
+ string localHostname,
+ Facility facility = default,
+ IPAddress localAddress = null,
+ int localPort = 0)
+ : this(
+ ResolveEndPoint(hostname, port),
+ localHostname,
+ facility,
+ localAddress,
+ localPort)
+ {
+ }
+
+ ///
+ /// Facility used by this Syslog client.
+ ///
+ public Facility Facility { get; }
+
+ ///
+ /// Hostname used in the 'Hostname' field in the Syslog message.
+ ///
+ public string LocalHostname { get; }
+
+ ///
+ /// Send a message to a SysLog server.
+ ///
+ /// of the message.
+ /// Message tag (will be followed by ': ' in the message).
+ /// Message content.
+ ///
+ public void SendMessage(
+ Severity severity,
+ string tag,
+ string content) => SendMessage(
+ Facility,
+ severity,
+ LocalHostname,
+ tag,
+ content);
+
+ ///
+ /// Send a generic Syslog message to a SysLog server.
+ ///
+ /// in the message
+ /// in the message
+ /// Hostname in the message
+ /// Message tag (will be followed by ': ' in the message).
+ /// Message content
+ ///
+ public void SendMessage(
+ Facility facility,
+ Severity severity,
+ string hostname,
+ string tag,
+ string content)
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(nameof(SyslogClient));
+ }
+
+ // We don't send message with Severity.None which isn't a RFC6134 valid value
+ if (severity != Severity.None)
+ {
+ _socket.Send(FormatUdpMessage(facility, severity, hostname, tag, content));
+ }
+ }
+
+ private static byte[] FormatUdpMessage(
+ Facility facility,
+ Severity severity,
+ string localHostname,
+ string messageTag,
+ string messageContent)
+ {
+ int priorityValue = ((int)facility * 8) + (int)severity;
+
+ var headerBuilder = new StringBuilder();
+
+ // RFC3164 PRI
+ headerBuilder.Append('<').Append(priorityValue).Append('>');
+
+ // RFC3164 HEADER
+ string timestamp = DateTime.UtcNow.ToString("MMM dd HH:mm:ss");
+ headerBuilder.Append(timestamp).Append(' ');
+ headerBuilder.Append(localHostname).Append(' ');
+
+ // RFC3164 MSG
+ if (messageTag != null)
+ {
+ headerBuilder.Append(messageTag).Append(':').Append(' ');
+ }
+
+ headerBuilder.Append(messageContent ?? "");
+ var s = headerBuilder.ToString();
+ return UTF8Encoding.UTF8.GetBytes(s); // Ideally an UTF7 is required here to be RFC3164 compliant
+ }
+
+ private static IPEndPoint ResolveEndPoint(
+ string hostname,
+ int port)
+ {
+ IPHostEntry hostEntry = Dns.GetHostEntry(hostname);
+ if (hostEntry is null)
+ {
+ throw new ArgumentException($"Hostname `{hostname}` is not resolvable");
+ }
+
+ return new IPEndPoint(hostEntry.AddressList[0], port);
+ }
+
+ ///
+ public void Dispose()
+ {
+ _disposed = true;
+ ((IDisposable)_socket)?.Dispose();
+ }
+ }
+}
diff --git a/nanoFramework.Logging.Syslog/SyslogLogger.cs b/nanoFramework.Logging.Syslog/SyslogLogger.cs
new file mode 100644
index 0000000..d78389c
--- /dev/null
+++ b/nanoFramework.Logging.Syslog/SyslogLogger.cs
@@ -0,0 +1,88 @@
+//
+// Copyright (c) .NET Foundation and Contributors
+// See LICENSE file in the project root for full license information.
+//
+
+using System;
+using System.Reflection;
+using Microsoft.Extensions.Logging;
+
+namespace nanoFramework.Logging.Syslog
+{
+ ///
+ /// implementation that send message log to a Syslog server following RFC3164 convention.
+ /// While not mandatory it is recommended to use to create the SyslogLogger instances.
+ ///
+ public class SyslogLogger : ILogger
+ {
+ private readonly SyslogClient _client;
+
+ ///
+ /// Create new logger based on existing . Recommended to use LoggerFactory pattern trough instead of direct use.
+ ///
+ /// to use for sending message.
+ /// CategoryName of this logger used as TAG in the Sys log messages.
+ ///
+ public SyslogLogger(
+ SyslogClient client,
+ string categoryName)
+ {
+ _client = client ?? throw new ArgumentNullException(nameof(client));
+ LoggerCategoryName = categoryName;
+ }
+
+ ///
+ /// Minimum log level used by this logger.
+ ///
+ public LogLevel MinLogLevel { get; set; } //by default 0 -> Trace
+
+ ///
+ /// CategoryName of this logger used as TAG in the Syslog messages.
+ ///
+ public string LoggerCategoryName { get; }
+
+ ///
+ /// Underlying used by this logger.
+ ///
+ public SyslogClient Client => _client;
+
+ ///
+ public bool IsEnabled(LogLevel logLevel) => logLevel >= MinLogLevel;
+
+ ///
+ public void Log(
+ LogLevel logLevel,
+ EventId eventId,
+ string state,
+ Exception exception,
+ MethodInfo format)
+ {
+ if (logLevel >= MinLogLevel && logLevel != LogLevel.None)
+ {
+ string message;
+ if (format is null)
+ {
+ message = exception == null ? $"{state}" : $"{state} {exception}";
+ }
+ else
+ {
+ message = $"{(string)format.Invoke(null, new object[] { LoggerCategoryName, logLevel, eventId, state, exception })}";
+ }
+
+ _client.SendMessage(LogLevelToSeverity(logLevel), LoggerCategoryName, message);
+ }
+ }
+
+ private Severity LogLevelToSeverity(LogLevel level) => level switch
+ {
+ LogLevel.Trace => Severity.Debug,
+ LogLevel.Debug => Severity.Debug,
+ LogLevel.Information => Severity.Informational,
+ LogLevel.Warning => Severity.Warning,
+ LogLevel.Error => Severity.Error,
+ LogLevel.Critical => Severity.Critical,
+ LogLevel.None => Severity.None,
+ _ => Severity.None
+ };
+ }
+}
diff --git a/nanoFramework.Logging.Syslog/SyslogLoggerFactory.cs b/nanoFramework.Logging.Syslog/SyslogLoggerFactory.cs
new file mode 100644
index 0000000..d424cb5
--- /dev/null
+++ b/nanoFramework.Logging.Syslog/SyslogLoggerFactory.cs
@@ -0,0 +1,86 @@
+//
+// Copyright (c) .NET Foundation and Contributors
+// See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.Extensions.Logging;
+using System;
+using System.Net;
+using System.Net.Sockets;
+
+namespace nanoFramework.Logging.Syslog
+{
+ ///
+ /// implementation that creates instances.
+ /// The logger factory initializes an underlying that will be shared by all instances it will create.
+ ///
+ public class SyslogLoggerFactory : ILoggerFactory
+ {
+ const string DefaultHostname = "nanoframework";
+ private readonly SyslogClient _client;
+ private bool _disposed;
+
+ ///
+ /// Create a logging factory that will provide support for Syslog ILogger provider
+ ///
+ /// Endpoint of the server.
+ /// 'Hostname' part of the RFC3164 message.
+ /// used by this logger.
+ /// Local IP address to bind to (if null will be used).
+ /// Local port to bind to (0 to choose available port).
+ public SyslogLoggerFactory(
+ IPEndPoint endpoint,
+ string localHostname = DefaultHostname,
+ Facility facility = default,
+ IPAddress localAddress = null,
+ int localPort = 0)
+ {
+ _client = new SyslogClient(endpoint, localHostname, facility, localAddress, localPort);
+ }
+
+ ///
+ /// Create a logging factory that will provide support for Syslog ILogger provider
+ ///
+ /// Fully Qualified Domain Name of the Syslog server.
+ /// Port of the Syslog server (default is 514).
+ /// 'Hostname' part of the RFC3164 message
+ /// used by this logger
+ /// Local IP address to bind to (if null will be used).
+ /// Local port to bind (0 to choose available port).
+ public SyslogLoggerFactory(
+ string hostname,
+ int port = SyslogClient.DefaultPort,
+ string localHostname = DefaultHostname,
+ Facility facility = default,
+ IPAddress localAddress = null,
+ int localPort = 0)
+ {
+ _client = new SyslogClient(
+ hostname,
+ port,
+ localHostname,
+ facility,
+ localAddress,
+ localPort);
+ }
+
+ ///
+ public ILogger CreateLogger(string categoryName)
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(nameof(SyslogLoggerFactory));
+ }
+
+ return new SyslogLogger(_client, categoryName);
+ }
+
+ ///
+ public void Dispose()
+ {
+ _disposed = true;
+ // If logger created through this factory are used after this you will get exception
+ _client?.Dispose();
+ }
+ }
+}
diff --git a/nanoFramework.Logging.Syslog/key.snk b/nanoFramework.Logging.Syslog/key.snk
new file mode 100644
index 0000000..67c9bb0
Binary files /dev/null and b/nanoFramework.Logging.Syslog/key.snk differ
diff --git a/nanoFramework.Logging.Syslog/nanoFramework.Logging.Syslog.nfproj b/nanoFramework.Logging.Syslog/nanoFramework.Logging.Syslog.nfproj
new file mode 100644
index 0000000..f56a396
--- /dev/null
+++ b/nanoFramework.Logging.Syslog/nanoFramework.Logging.Syslog.nfproj
@@ -0,0 +1,75 @@
+
+
+
+ $(MSBuildExtensionsPath)\nanoFramework\v1.0\
+
+
+
+ Debug
+ AnyCPU
+ {11A8DD76-328B-46DF-9F39-F559912D0360};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ a778ed84-6ea0-48c5-b97c-dc64a8d97a50
+ Library
+ Properties
+ 512
+ nanoFramework.Logging.Syslog
+ nanoFramework.Logging.Syslog
+ v1.0
+ bin\$(Configuration)\nanoFramework.Logging.Syslog.xml
+
+
+ true
+
+
+ key.snk
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\packages\nanoFramework.CoreLibrary.1.12.0-preview.1\lib\mscorlib.dll
+
+
+ ..\packages\nanoFramework.Runtime.Events.1.10.0-preview.4\lib\nanoFramework.Runtime.Events.dll
+
+
+ ..\packages\nanoFramework.System.Text.1.1.3-preview.7\lib\nanoFramework.System.Text.dll
+
+
+ ..\packages\nanoFramework.System.Net.1.8.0-preview.15\lib\System.Net.dll
+
+
+ ..\packages\nanoFramework.System.Threading.1.0.4-preview.8\lib\System.Threading.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.
+
+
+
+
\ No newline at end of file
diff --git a/nanoFramework.Logging.Syslog/packages.config b/nanoFramework.Logging.Syslog/packages.config
new file mode 100644
index 0000000..0fd4153
--- /dev/null
+++ b/nanoFramework.Logging.Syslog/packages.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nanoFramework.Logging.sln b/nanoFramework.Logging.sln
index 18d7f1b..6ef4703 100644
--- a/nanoFramework.Logging.sln
+++ b/nanoFramework.Logging.sln
@@ -1,4 +1,3 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31105.61
@@ -13,6 +12,8 @@ Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.Logging.Seria
EndProject
Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.Logging.Stream", "nanoFramework.Logging.Stream\nanoFramework.Logging.Stream.nfproj", "{38F918AD-5A39-4172-B4B4-ECC2A3FA5B4E}"
EndProject
+Project("{11A8DD76-328B-46DF-9F39-F559912D0360}") = "nanoFramework.Logging.Syslog", "nanoFramework.Logging.Syslog\nanoFramework.Logging.Syslog.nfproj", "{A778ED84-6EA0-48C5-B97C-DC64A8D97A50}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -43,6 +44,12 @@ Global
{38F918AD-5A39-4172-B4B4-ECC2A3FA5B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38F918AD-5A39-4172-B4B4-ECC2A3FA5B4E}.Release|Any CPU.Build.0 = Release|Any CPU
{38F918AD-5A39-4172-B4B4-ECC2A3FA5B4E}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {A778ED84-6EA0-48C5-B97C-DC64A8D97A50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A778ED84-6EA0-48C5-B97C-DC64A8D97A50}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A778ED84-6EA0-48C5-B97C-DC64A8D97A50}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {A778ED84-6EA0-48C5-B97C-DC64A8D97A50}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A778ED84-6EA0-48C5-B97C-DC64A8D97A50}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A778ED84-6EA0-48C5-B97C-DC64A8D97A50}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE