├── LICENSE ├── README.md └── Syslog.cs /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jitbit (the company behind "Jitbit Helpdesk" software) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SyslogCore 2 | 3 | Simple way to write to `syslog` aka `/dev/log` aka `/var/log/syslog` in .NET Core on Linux. Consists of [one short C# file](Syslog.cs) (70 lines!) that you can throw into your project. Tested in ASP.NET Core 5. 4 | 5 | ## Usage 6 | 7 | ```csharp 8 | Syslog.Write(Syslog.Level.Warning, "MyAwesomeApp", "something went wrong"); 9 | ``` 10 | 11 | ## The problem 12 | 13 | .NET Core (aka .NET 5 and later) does not have a [built-in](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0&tabs=aspnetcore2x#built-in-logging-providers-1) logging provider for linux. The official recommendation is to use a 3rd party logger, like Serilog, NLog, Log4Net etc. 14 | 15 | All heavyweight large libraries. 16 | 17 | There's an [ongoing discussion](https://github.com/aspnet/Logging/issues/441) if a default logger should be a part of .NET runtime, but it's stall. 18 | 19 | ## File logging is tricky 20 | 21 | Where do you place the logs? How do you grant permissions to that location? How should the files be formatted? When do the files rotate/roll over? Microsoft couldn't decide and... simply ignored the problem. 22 | 23 | ## Enter `syslog` 24 | 25 | **Why reinvent the wheel?!** 26 | 27 | Almost every linux distro comes with a built-in feature called `syslog`. It takes care of everything: queues messages, writes to log files, rotates files (via "logrotate"), and exists on literally every Linux machine. It has lots of ways to send messages to it: UDP-listener, TCP-listener, a "Unix socket" at `/dev/log`, a `logger` CLI command or a `syslog()` system function etc. 28 | 29 | For Windows folks: think of it as an `EventLog.Write`, *but for Linux* 30 | 31 | ## Works with docker too! 32 | 33 | If you're running an app inside a container, `docker logs` will show these logs, zero configuration. 34 | 35 | ## How do we use it in C#? 36 | 37 | Just use the good old `DllImport` to reference the external `libc` library, and call the original [syslog](https://linux.die.net/man/3/syslog) function. That's it. No Nugets, no dependency-injection. 38 | 39 | Happy coding. 40 | 41 | # FAQ 42 | 43 | ### 1. What if (for some weird reason) syslog is not installed on my Linux? 44 | 45 | Run `sudo apt-get install rsyslog` 46 | 47 | ### 2. Syslog is present on my machine but there's no logs 48 | 49 | `rsyslog` might be present but it's _not running_ (known issue with WSL2 for example). Check that its running, if not - start it: 50 | 51 | ``` 52 | $ service rsyslog status 53 | * rsyslogd is not running 54 | $ sudo service rsyslog start 55 | ``` 56 | 57 | Then test that logging actually works: 58 | 59 | ``` 60 | $ logger testtesttest 61 | $ tail -1 /var/log/syslog 62 | Oct 11 13:51:18 DESKTOP-CDBR5NK jazz: testtesttest 63 | ``` 64 | 65 | ### 3. What if I copy paste this into a cross-platform app that runs on both Windows and Linux? 66 | 67 | No worries, the code checks if it runs on Windows or Linux before proceeding. 68 | -------------------------------------------------------------------------------- /Syslog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Jitbit.Utils 5 | { 6 | public class Syslog 7 | { 8 | [Flags] 9 | private enum Option 10 | { 11 | Pid = 0x01, 12 | Console = 0x02, 13 | Delay = 0x04, 14 | NoDelay = 0x08, 15 | NoWait = 0x10, 16 | PrintError = 0x20 17 | } 18 | 19 | [Flags] 20 | private enum Facility 21 | { 22 | User = 1 << 3, //removed other unused enum values for brevity 23 | } 24 | 25 | [Flags] 26 | public enum Level 27 | { 28 | Emerg = 0, 29 | Alert = 1, 30 | Crit = 2, 31 | Err = 3, 32 | Warning = 4, 33 | Notice = 5, 34 | Info = 6, 35 | Debug = 7 36 | } 37 | 38 | [DllImport("libc")] 39 | private static extern void openlog(IntPtr ident, Option option, Facility facility); 40 | 41 | [DllImport("libc")] 42 | private static extern void syslog(int priority, string message); 43 | 44 | [DllImport("libc")] 45 | private static extern void closelog(); 46 | 47 | public static void Write(Syslog.Level level, string identity, string message) 48 | { 49 | //are we on linux? 50 | if (!OperatingSystem.IsLinux()) return; 51 | 52 | //validate input 53 | if (string.IsNullOrWhiteSpace(message) && string.IsNullOrWhiteSpace(identity)) return; 54 | 55 | IntPtr ident = Marshal.StringToHGlobalAnsi(identity); 56 | openlog(ident, Option.Console | Option.Pid | Option.PrintError, Facility.User); 57 | 58 | //split multiline messages, otherwise we end up with "line1 #012 line2 #012 line3" etc 59 | foreach (var line in message.Split('\n', StringSplitOptions.RemoveEmptyEntries)) 60 | { 61 | syslog((int)Facility.User | (int)level, line.Trim()); 62 | } 63 | 64 | closelog(); 65 | Marshal.FreeHGlobal(ident); 66 | } 67 | } 68 | } 69 | --------------------------------------------------------------------------------