diff --git a/.gitignore b/.gitignore index cfe647e8..f6d6dd75 100644 --- a/.gitignore +++ b/.gitignore @@ -4,25 +4,49 @@ # User-specific files *.suo *.user +*.userosscache *.sln.docstates -# Build results +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs +# Build results [Dd]ebug/ +[Dd]ebugPublic/ [Rr]elease/ +[Rr]eleases/ x64/ -build/ +x86/ +bld/ [Bb]in/ [Oo]bj/ -src/NLog.Targets.Syslog/lib -src/NLog.Targets.Syslog/deploy +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + *_i.c *_p.c +*_i.h *.ilk *.meta *.obj @@ -42,13 +66,17 @@ src/NLog.Targets.Syslog/deploy *.vssscc .builds *.pidb -*.log +*.svclog *.scc +# Chutzpah Test files +_Chutzpah* + # Visual C++ cache files ipch/ *.aps *.ncb +*.opendb *.opensdf *.sdf *.cachefile @@ -57,6 +85,10 @@ ipch/ *.psess *.vsp *.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ # Guidance Automation Toolkit *.gpState @@ -64,6 +96,10 @@ ipch/ # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode # TeamCity is a build add-in _TeamCity* @@ -72,8 +108,16 @@ _TeamCity* *.dotCover # NCrunch -*.ncrunch* +_NCrunch_* .*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ # Installshield output folder [Ee]xpress/ @@ -92,65 +136,115 @@ DocProject/Help/html publish/ # Publish Web Output -*.Publish.xml +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted *.pubxml - -# NuGet Packages Directory -## TODO: If you have NuGet Package Restore enabled, uncomment the next line -packages/ - -# Windows Azure Build Output -csx +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ *.build.csdef -# Windows Store app package directory +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ # Others -sql/ -*.Cache ClientBin/ -[Ss]tyle[Cc]op.* ~$* *~ *.dbmdl -*.[Pp]ublish.xml +*.dbproj.schemaview *.pfx *.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ # RIA/Silverlight projects Generated_Code/ -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files -App_Data/*.mdf -App_Data/*.ldf +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml +# Node.js Tools for Visual Studio +.ntvs_analysis.dat -#LightSwitch generated files -GeneratedArtifacts/ -_Pvt_Extensions/ -ModelManifest.xml +# Visual Studio 6 build log +*.plg -# ========================= -# Windows detritus -# ========================= +# Visual Studio 6 workspace options file +*.opt -# Windows image file caches -Thumbs.db -ehthumbs.db +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions -# Folder config file -Desktop.ini +# Paket dependency manager +.paket/paket.exe +paket-files/ -# Recycle Bin used on file shares -$RECYCLE.BIN/ +# FAKE - F# Make +.fake/ -# Mac desktop service store files -.DS_Store +# JetBrains Rider +.idea/ +*.sln.iml diff --git a/README.md b/README.md index 89936ac3..9a8eb13e 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,38 @@ -Syslog Target for NLog +Syslog target for NLog ====================== +**NLog Syslog** is a custom target for **NLog**: [http://nlog-project.org](http://nlog-project.org/). + +It can be used with version 4.3.2 and later of NLog and allows to send logging messages to a Unix-style Syslog server. -**NLog Syslog** is a custom target for [NLog](http://nlog-project.org/) version 4.2.2 allowing you to send logging messages to a UNIX-style Syslog server. -## Configuration -To use NLog Syslog, you simply wire it up as an extension in the NLog.config file and place the NLog.Targets.Syslog.dll in the same location as the NLog.dll & NLog.config files. Then use as you would any NLog target. To use TCP as transport protocol just specify protocol="tcp" in target configuration. Below is a sample NLog.config file: +## Configuration +To use NLog Syslog, you simply wire it up as an extension in the NLog.config file and place the NLog.Targets.Syslog.dll in the same location as the NLog.dll & NLog.config files. +Then use as you would any NLog target. +Below is a sample NLog.config file: ```xml - + - - - - - - + + + + Local4 + Rfc5424 + + + + + + true + + + - - + ``` @@ -29,60 +40,324 @@ The package is also available through NuGet. Simply search for "NLog.Targets.Sys ### Options +This NLog target supports the standard NLog [layout](https://github.com/NLog/NLog/wiki/Layouts) +directive to modify the log message body (Syslog packet elements are not affected). +It provides default values for all configuration options which can be overridden +by means of configuration settings, as shown in the example configuration above. +A more detailed example is included in the [test application](./src/TestApp/NLog.config). + +#### Enforcement element +* `splitOnNewLine` - `false` or `true` to split log entries on new line (default: `false`) +* `transliterate` - `false` or `true` to trasliterate strings from Unicode to ASCII when the RFC allows only ASCII characters for a fields (default: `false`) +* `replaceInvalidCharacters` - `false` or `true` to replace invalid values usually with a question mark (default: `false`) +* `truncateFieldsToMaxLength` - `false` or `true` to truncate fields to the length specified in the RFC (default: `false`) +* `truncateMessageTo` - a number specifying the max lenght allowed for the whole message (default: `0` i.e. do not truncate) + +The maximum length of a message is detailed in many RFCs that can be summarized as follow: + +| | MUST be supported | SHOULD be supported | MUST NOT exceed (we are limited by Int32.MaxValue) +| :-------------------: | :-----------------: | :-------------------: | :--------------------------------------------------: +| RFC 3164 (UDP) | 1024 B | 1024 B | 1024 B +| RFC 6587 (TCP) | 1024 B | 1024 B | 1024 B +| RFC 5424 (TCP/UDP) | 480 B | 2048 B | - +| RFC 5426 (UDP/IPv4) | 480 B | 2048 B | 65535 - 60 - 8 B 1 +| RFC 5426 (UDP/IPv6) | 1180 B | 2048 B | 65535 - 40 - 8 B 1 +| RFC 5426 (UDP/IPv6) | 1180 B | 2048 B | (2^32 - 1) - 40 - 8 B 1 2 +| RFC 5425 (TLS/IPv4) | 2048 B | 8192 B | 65535 - 60 - 60 B 1 +| RFC 5425 (TLS/IPv6) | 2048 B | 8192 B | 65535 - 40 - 60 B 1 +| RFC 5425 (TLS/IPv6) | 2048 B | 8192 B | (2^32 - 1) - 40 - 60 B 1 2 + +1 IP payload - max IP header - max protocol header +2 Using jumbograms + +#### Message builder element +* `facility` - facility name (default: `Local1`) +* `rfc` - `rfc3164` or `rfc5424` (default: `rfc5424`) +* `rfc3164` - settings related to RFC 3164: + * `hostname` ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) - the HOSTNAME part (default: the hostname of the computer that is creating the message) + * `tag` ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) - the TAG part (default: the name of the assembly that is creating the message) +* `rfc5424` - settings related to RFC 5424: + * `hostname` ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) - the HOSTNAME field of the HEADER part (default: the hostname of the computer that is creating the message) + * `appName` ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) - the APPNAME field of the HEADER part (default: the name of the assembly that is creating the message) + * `procId` ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) - the PROCID field of the HEADER part (default: `-`) + * `msgId` ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) - the MSGID field of the HEADER part (default: `-`) + * `structuredData` - the STRUCTURED-DATA part containing the SD-ELEMENTs each composed by an SD-ID ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) + and optional SD-PARAM fields, i.e. the PARAM-NAME ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) and + PARAM-VALUE ([Layout](http://github.com/NLog/NLog/wiki/Layouts)) fields (default: `-`).
+ The fromEventProperties attribute allows to use [log event properties data](http://github.com/NLog/NLog/wiki/EventProperties-Layout-Renderer) + enabling different STRUCTURED-DATA for each log message + * `disableBom` - `true` or `false` to handle RSyslog [issue 284](http://github.com/rsyslog/rsyslog/issues/284) (default: `false`) + +#### Message transmitter element +* `protocol` - `udp` or `tcp` (default: `udp`) +* `udpProtocol` - settings related to UDP: + * `server` - IP or hostname of the Syslog server (default: `127.0.0.1`) + * `port` - port the Syslog server is listening on (default: `514`) +* `tcpProtocol` - settings related to TCP: + * `server` - IP or hostname of the Syslog server (default: `127.0.0.1`) + * `port` - port the Syslog server is listening on (default: `514`) + * `useTls` - `false` or `true` (default: `true`) + * `framing` - `nonTransparent` or `octectCounting` (default: `octectCounting`) + + + +
+
+ + + +# Test bench + 1. `[HOST]` Download VirtualBox and Vagrant and install them + 2. `[HOST]` Download an [Ubuntu Vagrant box](http://cloud-images.ubuntu.com/vagrant/) + 3. `[HOST]` Create a Vagrantfile in the same folder of the downloaded box + + ```ruby + Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/trusty64" + config.ssh.host = "127.0.0.1" + config.ssh.username = "vagrant" + config.ssh.password = "vagrant" + config.vm.network :forwarded_port, id: 'ssh', guest: 22, host: 2222, auto_correct: false + config.vm.network :forwarded_port, guest: 514, host: 1514, protocol: "tcp", auto_correct: false + config.vm.network :forwarded_port, guest: 514, host: 1514, protocol: "udp", auto_correct: false + end + ``` + + 5. `[HOST]` Add the box to the list + + ```shell + vagrant box add .\ubuntuvagrant.box --name 'ubuntu/trusty64' + vagrant box list + ``` + + 6. `[HOST]` Start the VM + + ```shell + vagrant up + ``` + + 7. `[HOST]` Connect to the VM with SSH on port 2222 + 8. `[GUEST]` Switch to the root user + + ```shell + su + ``` + + 9. `[GUEST]` Uncomment the following `/etc/rsyslog.conf` lines: + + ``` + #$ModLoad imudp + #$UDPServerRun 514 + ``` + ``` + #$ModLoad imtcp + #$InputTCPServerRun 514 + ``` + +10. `[GUEST]` Add the following `/etc/rsyslog.d/50-default.conf` line under the `user.*` one (prefixing a path with the minus sign omits flushing after every log event) + + ``` + local4.* /var/log/local4.log + ``` + +11. `[GUEST]` Restart Syslog service + + ```shell + service rsyslog restart + ``` + +12. `[HOST]` Restart the VM -This NLog target provides default values for all configuration options. -Optionally, your configuration can override them using attributes on -`target`, as shown in the example configuration above. + ```shell + vagrant reload + ``` -#### Destination +13. `[GUEST]` Make sure rsyslog is running -* `syslogserver`: IP or hostname (default: `127.0.0.1`) -* `port`: Port of syslog listener (default: `514`) -* `protocol`: `udp` or `tcp` (default: `udp`) -* `ssl`: `false` or `true`; TCP only (default: `false`) -* `rfc`: Rfc compatibility for syslog message `Rfc3164` or `Rfc5424` (default: `Rfc3164`) + ```shell + ps -A | grep rsyslog + ``` -#### Syslog packet elements +14. `[GUEST]` Check the rsyslog configuration -Messages are sent using the format (framing) called syslog, which is -defined in [RFC 3164](http://www.ietf.org/rfc/rfc3164.txt) or -[RFC 5424](http://tools.ietf.org/html/rfc5424). In addition -to a timestamp and the log message, RFC 3164 syslog messages include -other elements: sending device name (such as the machine's hostname), -sending app/component name (called "tag" in the RFC), facility, and -severity. + ```shell + rsyslogd -N1 + ``` -The following syslog elements can be overridden for RFC 3164: +15. `[GUEST]` Check the Linux system log for rsyslog errors -* `machinename` ([Layout](https://github.com/NLog/NLog/wiki/Layouts)): name of sending system or entity (default: machine - [hostname](http://msdn.microsoft.com/en-us/library/system.net.dns.gethostname(v=vs.110).aspx)). -For example, ${machinename} -* `sender` ([Layout](https://github.com/NLog/NLog/wiki/Layouts)): name of sending component or application (default: - [calling method](http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getcallingassembly(v=vs.110).aspx)). -For example, ${logger} -* `facility`: facility name (default: `Local1`) + ```shell + cat /var/log/syslog | grep rsyslog + ``` -For example, to make logs from multiple systems use the same device -identifier (rather than each system's hostname), one could set -`machinename` to `app-cloud`. The logs from different systems would -all appear to be from the same single entity called `app-cloud`. +16. `[GUEST]` Perform a local test -The following additional syslog elements can be overridden for [RFC 5424](http://tools.ietf.org/html/rfc5424): + ```shell + logger --server 127.0.0.1 --port 514 --priority local4.error "TCP local test" + logger --server 127.0.0.1 --port 514 --priority local4.warning --udp "UDP local test" + tail -3 /var/log/syslog + tail -3 /var/log/local4.log + ``` -* `procid` ([Layout](https://github.com/NLog/NLog/wiki/Layouts)): [identifier](http://tools.ietf.org/html/rfc5424#section-6.2.6) (numeric or alphanumeric) of sending entity -(default: -). For example, ${processid} or ${processname} -* `msgid` ([Layout](https://github.com/NLog/NLog/wiki/Layouts)): [message type identifier](http://tools.ietf.org/html/rfc5424#section-6.2.7) (numeric or alphanumeric) of sending entity -(default: -). For example, ${callsite} -* `structureddata` ([Layout](https://github.com/NLog/NLog/wiki/Layouts)): [additional data](http://tools.ietf.org/html/rfc5424#section-6.3) of sending entity (default: -). -For example, [thread@12345 id="${threadid}" name="${threadname}"][mydata2@12345 num="1" code="mycode"] +17. `[GUEST]` Prepare for a remote test -#### Log message body + ```shell + tail -f /var/log/syslog + ``` -This target supports the standard NLog -[layout](https://github.com/NLog/NLog/wiki/Layouts) directive to modify -the log message body. The syslog packet elements are not affected. + OR + ```shell + tcpdump port 514 -vv + ``` + +18. `[HOST]` Perform a remote test + + ```shell + telnet 127.0.0.1 1514 + ``` + +19. `[HOST]` Perform a remote test with the NLog target (configuring it to use the Local4 facility) + + + +
+
+ + + +# Syslog message format +Messages are built using the format defined in +[RFC 3164](http://tools.ietf.org/html/rfc3164) or +[RFC 5424](http://tools.ietf.org/html/rfc5424). +They are then sent using one of the protocols defined in +[RFC 5426](http://tools.ietf.org/html/rfc5426) or +[RFC 6587](http://tools.ietf.org/html/rfc6587) or +[RFC 5425](http://tools.ietf.org/html/rfc5425). + + +### RFC 3164 +There are no set requirements on the contents of the Syslog message: the payload of any Syslog message must be considered to be a valid Syslog message. +It is, however, recommended for the Syslog message to have all the parts described here. + +#### Conventions +* `SPACE`: the ASCII value `dec 32` / `hex 20` +* `PRINTUSASCII`: ASCII values in the range `dec [33, 126]` / `hex [21, 7E]` + +#### Message parts +A Syslog message is at least 1 and at most 1024 characters long and the only allowed characters are `SPACE` or `PRINTUSASCII` + +``` +SYSLOG MESSAGE = PRI HEADER SPACE MSG + +PRI = < PRIVAL > + PRIVAL = FACILITY * 8 + SEVERITY + FACILITY + A number between 0 and 23 + SEVERITY + A number between 0 and 7 + +HEADER = TIMESTAMP space HOSTNAME (only SPACE or PRINTUSASCII allowed) + TIMESTAMP + "Mmm dd hh:mm:ss" using a local timezone + Space-padding in dd, zero-padding in hh, mm and ss + HOSTNAME + Hostname or IPv4 address or IPv6 address of the sender machine + +MSG = TAG CONTENT + TAG + Name of the sender program or process + An alphanumeric string not exceeding 32 characters + CONTENT + Detailed information of the event + A non-alphanumeric character followed by SPACE or PRINTUSASCII characters +``` + +#### Examples + +* `<34>Oct 11 00:14:05 mymachine su: 'su root' failed for lonvick on /dev/pts/8` +* `<13>Feb 5 17:32:18 10.0.0.99 myTag Use the BFG!` + + +### RFC 5424 + + +#### Conventions +* `(section)`: brackets are used to indicate that a section is optional +* `NILVALUE`: the hyphen i.e. ASCII value dec 45 / hex 2D +* `SPACE`: the ASCII value `dec 32` / `hex 20` +* `PRINTUSASCII`: ASCII values in the range `dec [33, 126]` / `hex [21, 7E]` +* `SAFEPRINTUSASCII`: PRINTUSASCII except `=`, `]`, `"` + +#### Message parts +A Syslog message is at most 480 to 2048 or more bytes + +This is the detail of the format: +``` +SYSLOG MESSAGE = HEADER SPACE STRUCTURED-DATA (SPACE MSG) + +HEADER = PRI VERSION SPACE TIMESTAMP SPACE HOSTNAME SPACE APPNAME SPACE PROCID SPACE MSGID + PRI = < PRIVAL > + PRIVAL = FACILITY * 8 + SEVERITY + FACILITY + A number between 0 and 23 + SEVERITY + A number between 0 and 7 + VERSION + A nonzero digit followed by 0 to 2 digits (current version is 1) + TIMESTAMP + NILVALUE or RFC3339 timestamp with an optional 1 to 6 digits second fraction part + HOSTNAME + NILVALUE or 1 to 255 PRINTUSASCII + The FQDN or IPv4 address or IPv6 address or hostname of the sender machine + APPNAME + NILVALUE or 1 to 48 PRINTUSASCII + The device or application sending the Syslog message + PROCID + NILVALUE or 1 to 128 PRINTUSASCII + A change indicates a discontinuity in Syslog reporting + Often the process name or id or an identifier of the group the Syslog message belongs to + MSGID + NILVALUE or 1 to 32 PRINTUSASCII + The type of message that should be the same for events with the same semantics + +STRUCTURED-DATA = NILVALUE or one or more SD-ELEMENT + SD-ELEMENT = [ SD-ID (one or more SPACE SD-PARAM) ] + SD-ID + At most 32 SAFEPRINTUSASCII specifying a unique identifier within STRUCTUREDDATA + The identifier can be a CUSTOMID or IANAID: + CUSTOMID = NAME @ PEN + NAME + SAFEPRINTUSASCII except @ + PEN + A private enterprise number + Digits or digits separated by periods + IANAID = timeQuality or origin or meta + timeQuality + Parameters are tzKnown, isSynced, syncAccuracy + origin + Parameters are ip, enterpriseId, software, swVersion + meta + Parameters are sequenceId, sysUpTime, language + SD-PARAM = PARAM-NAME = " PARAM-VALUE " + PARAM-NAME + 1 to 32 SAFEPRINTUSASCII + PARAM-VALUE + UTF8 STRING with ", \ and ] escaped as \", \\ and \] + +MSG = MSGANY or MSGUTF8 + MSGANY + Zero or more bytes + MSGUTF8 = BOM UTF8STRING + BOM + The hex value EFBBBF + UTF8STRING + A UTF8 compliant string +``` -## NLog +#### Examples -See more about NLog at: http://nlog-project.org +* `<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - BOM'su root' failed for lonvick on /dev/pts/8` +* `<165>1 2003-08-24T05:14:15.000003-07:00 192.0.2.1 myproc 8710 - - %% It's time to make the do-nuts.` +* `<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] BOMAn application event log entry...` +* `<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"][examplePriority@32473 class="high"]` \ No newline at end of file diff --git a/src/NLog.Targets.Syslog.sln b/src/NLog.Targets.Syslog.sln index 2deadff3..843ecb99 100644 --- a/src/NLog.Targets.Syslog.sln +++ b/src/NLog.Targets.Syslog.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +VisualStudioVersion = 14.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.Syslog", "NLog.Targets.Syslog\NLog.Targets.Syslog.csproj", "{D888EE7E-4394-4FB1-9B37-29513FF8A91A}" EndProject @@ -10,39 +10,24 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9B0D1054-6E68-4208-97BC-12F254CF3754}" ProjectSection(SolutionItems) = preProject License.txt = License.txt + schemas\NLog.Targets.Syslog.xsd = schemas\NLog.Targets.Syslog.xsd ..\README.md = ..\README.md EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Debug|x86.ActiveCfg = Debug|Any CPU {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Release|Any CPU.ActiveCfg = Release|Any CPU {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Release|Any CPU.Build.0 = Release|Any CPU - {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {D888EE7E-4394-4FB1-9B37-29513FF8A91A}.Release|x86.ActiveCfg = Release|Any CPU - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Debug|Any CPU.ActiveCfg = Debug|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Debug|x86.ActiveCfg = Debug|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Debug|x86.Build.0 = Debug|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Release|Any CPU.ActiveCfg = Release|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Release|Mixed Platforms.Build.0 = Release|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Release|x86.ActiveCfg = Release|x86 - {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Release|x86.Build.0 = Release|x86 + {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEDBC211-287E-4D64-B111-594D1CB7C54B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/NLog.Targets.Syslog/MessageCreation/EncodingSet.cs b/src/NLog.Targets.Syslog/MessageCreation/EncodingSet.cs new file mode 100644 index 00000000..b648a14c --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/EncodingSet.cs @@ -0,0 +1,16 @@ +using System.Text; + +namespace NLog.Targets.Syslog.MessageCreation +{ + internal class EncodingSet + { + public ASCIIEncoding Ascii { get; } + public UTF8Encoding Utf8 { get; } + + public EncodingSet(bool enableBom) + { + Ascii = new ASCIIEncoding(); + Utf8 = new UTF8Encoding(enableBom); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/EnumerableExtension.cs b/src/NLog.Targets.Syslog/MessageCreation/EnumerableExtension.cs new file mode 100644 index 00000000..45d111ed --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/EnumerableExtension.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace NLog.Targets.Syslog.MessageCreation +{ + internal static class EnumerableExtension + { + public static void ForEach(this IEnumerable enumerable, Action action) + { + foreach (var item in enumerable) + action(item); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/SyslogFacility.cs b/src/NLog.Targets.Syslog/MessageCreation/Facility.cs similarity index 93% rename from src/NLog.Targets.Syslog/SyslogFacility.cs rename to src/NLog.Targets.Syslog/MessageCreation/Facility.cs index 8ef0b577..f08aad75 100644 --- a/src/NLog.Targets.Syslog/SyslogFacility.cs +++ b/src/NLog.Targets.Syslog/MessageCreation/Facility.cs @@ -1,9 +1,7 @@ -// ReSharper disable CheckNamespace -namespace NLog.Targets -// ReSharper enable CheckNamespace +namespace NLog.Targets.Syslog.MessageCreation { /// Syslog facilities - public enum SyslogFacility + public enum Facility { /// Kernel messages Kernel = 0, diff --git a/src/NLog.Targets.Syslog/MessageCreation/MessageBuilder.cs b/src/NLog.Targets.Syslog/MessageCreation/MessageBuilder.cs new file mode 100644 index 00000000..ed004ae9 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/MessageBuilder.cs @@ -0,0 +1,46 @@ +using NLog.Layouts; +using NLog.Targets.Syslog.Policies; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace NLog.Targets.Syslog.MessageCreation +{ + /// Allows to build Syslog messages + public abstract class MessageBuilder + { + private SplitOnNewLinePolicy splitOnNewLinePolicy; + + /// The Syslog facility to log from (its name e.g. local0 or local7) + protected Facility Facility { get; set; } + + internal virtual void Initialize(Enforcement enforcement, Facility facility) + { + splitOnNewLinePolicy = new SplitOnNewLinePolicy(enforcement); + Facility = facility; + } + + internal IEnumerable> BuildMessages(LogEventInfo logEvent, Layout layout) + { + var pri = Pri(Facility, (Severity)logEvent.Level); + var logEntries = LogEntries(logEvent, layout).ToList(); + var toBeSent = logEntries.Select(logEntry => BuildMessage(logEvent, pri, logEntry)); + return toBeSent; + } + + internal abstract IEnumerable BuildMessage(LogEventInfo logEvent, string pri, string logEntry); + + private static string Pri(Facility facility, Severity severity) + { + var priVal = (int)facility * 8 + (int)severity; + var priValString = priVal.ToString(CultureInfo.InvariantCulture); + return $"<{priValString}>"; + } + + private IEnumerable LogEntries(LogEventInfo logEvent, Layout layout) + { + var originalLogEntry = layout.Render(logEvent); + return splitOnNewLinePolicy.IsApplicable() ? splitOnNewLinePolicy.Apply(originalLogEntry) : new[] { originalLogEntry }; + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/MessageBuildersFacade.cs b/src/NLog.Targets.Syslog/MessageCreation/MessageBuildersFacade.cs new file mode 100644 index 00000000..2f2d806f --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/MessageBuildersFacade.cs @@ -0,0 +1,49 @@ +using NLog.Layouts; +using NLog.Targets.Syslog.Policies; +using System.Collections.Generic; + +namespace NLog.Targets.Syslog.MessageCreation +{ + public class MessageBuildersFacade + { + private MessageBuilder activeBuilder; + private readonly Dictionary builders; + + /// The Syslog facility to log from (its name e.g. local0 or local7) + public Facility Facility { get; set; } + + /// The Syslog protocol RFC to be followed + public RfcNumber Rfc { get; set; } + + /// RFC 3164 related fields + public Rfc3164 Rfc3164 { get; set; } + + /// RFC 5424 related fields + public Rfc5424 Rfc5424 { get; set; } + + /// Builds a new instance of the MessageBuildersFacade class + public MessageBuildersFacade() + { + Facility = Facility.Local1; + Rfc = RfcNumber.Rfc5424; + Rfc3164 = new Rfc3164(); + Rfc5424 = new Rfc5424(); + builders = new Dictionary + { + { RfcNumber.Rfc3164, Rfc3164 }, + { RfcNumber.Rfc5424, Rfc5424 } + }; + } + + internal void Initialize(Enforcement enforcement) + { + activeBuilder = builders[Rfc]; + activeBuilder.Initialize(enforcement, Facility); + } + + internal IEnumerable> BuildMessages(LogEventInfo logEvent, Layout layout) + { + return activeBuilder.BuildMessages(logEvent, layout); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/Rfc3164.cs b/src/NLog.Targets.Syslog/MessageCreation/Rfc3164.cs new file mode 100644 index 00000000..1518cbe5 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/Rfc3164.cs @@ -0,0 +1,86 @@ +using NLog.Config; +using NLog.Layouts; +using NLog.Targets.Syslog.Policies; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; + +namespace NLog.Targets.Syslog.MessageCreation +{ + /// Allows to build Syslog messages compliant with RFC 3164 + [NLogConfigurationItem] + public class Rfc3164 : MessageBuilder + { + private PlainHostnamePolicySet hostnamePolicySet; + private TagPolicySet tagPolicySet; + private PlainContentPolicySet plainContentPolicySet; + private EncodedContentPolicy encodedContentPolicy; + private const string TimestampFormat = "{0:MMM} {0,11:d HH:mm:ss}"; + private static readonly byte[] SpaceBytes = { 0x20 }; + + /// The HOSTNAME field of the HEADER part + public Layout Hostname { get; set; } + + /// The TAG field of the MSG part + public Layout Tag { get; set; } + + /// Builds a new instance of the Rfc3164 class + public Rfc3164() + { + Hostname = Dns.GetHostName(); + Tag = Assembly.GetCallingAssembly().GetName().Name; + } + + internal override void Initialize(Enforcement enforcement, Facility facility) + { + base.Initialize(enforcement, facility); + hostnamePolicySet = new PlainHostnamePolicySet(enforcement); + tagPolicySet = new TagPolicySet(enforcement); + plainContentPolicySet = new PlainContentPolicySet(enforcement); + encodedContentPolicy = new EncodedContentPolicy(enforcement); + } + + internal override IEnumerable BuildMessage(LogEventInfo logEvent, string pri, string logEntry) + { + var encoding = new ASCIIEncoding(); + var msgPrefixBytes = PriBytes(pri, encoding) + .Concat(HeaderBytes(logEvent, encoding)) + .Concat(SpaceBytes) + .ToArray(); + var msgBytes = MsgBytes(logEvent, logEntry, msgPrefixBytes.Length, encoding); + return msgPrefixBytes.Concat(msgBytes); + } + + private static IEnumerable PriBytes(string pri, Encoding encoding) + { + return encoding.GetBytes(pri); + } + + private IEnumerable HeaderBytes(LogEventInfo logEvent, Encoding encoding) + { + var timestamp = string.Format(CultureInfo.InvariantCulture, TimestampFormat, logEvent.TimeStamp); + var hostname = hostnamePolicySet.Apply(Hostname.Render(logEvent)); + var header = $"{timestamp} {hostname}"; + return encoding.GetBytes(header); + } + + private IEnumerable MsgBytes(LogEventInfo logEvent, string logEntry, int msgPrefixSize, Encoding encoding) + { + var tag = tagPolicySet.Apply(Tag.Render(logEvent)); + var tagBytes = encoding.GetBytes(tag); + var contentPrefixLength = msgPrefixSize + tag.Length; + var contentBytes = ContentBytes(logEntry, contentPrefixLength, encoding); + return tagBytes.Concat(contentBytes); + } + + private IEnumerable ContentBytes(string logEntry, int contentPrefixLength, Encoding encoding) + { + var plainContent = plainContentPolicySet.Apply(logEntry); + var encodedContent = encoding.GetBytes(plainContent); + return encodedContentPolicy.Apply(encodedContent, contentPrefixLength); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/Rfc5424.cs b/src/NLog.Targets.Syslog/MessageCreation/Rfc5424.cs new file mode 100644 index 00000000..ee12df23 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/Rfc5424.cs @@ -0,0 +1,117 @@ +using NLog.Config; +using NLog.Layouts; +using NLog.Targets.Syslog.Policies; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Reflection; + +namespace NLog.Targets.Syslog.MessageCreation +{ + /// Allows to build Syslog messages compliant with RFC 5424 + [NLogConfigurationItem] + public class Rfc5424 : MessageBuilder + { + private readonly string defaultHostname; + private readonly string defaultAppName; + private const string DefaultVersion = "1"; + private const string NilValue = "-"; + private FqdnHostnamePolicySet hostnamePolicySet; + private AppNamePolicySet appNamePolicySet; + private ProcIdPolicySet procIdPolicySet; + private MsgIdPolicySet msgIdPolicySet; + private MsgWithoutPreamblePolicy msgWithoutPreamblePolicy; + private const string TimestampFormat = "{0:yyyy-MM-ddTHH:mm:ss.ffffffK}"; + private static readonly byte[] SpaceBytes = { 0x20 }; + + /// The VERSION field of the HEADER part + public string Version { get; } + + /// The HOSTNAME field of the HEADER part + public Layout Hostname { get; set; } + + /// The APPNAME field of the HEADER part + public Layout AppName { get; set; } + + /// The PROCID field of the HEADER part + public Layout ProcId { get; set; } + + /// The MSGID field of the HEADER part + public Layout MsgId { get; set; } + + /// The STRUCTURED-DATA part + public StructuredData StructuredData { get; set; } + + /// Whether to remove or not BOM in the MSG part + /// RSyslog issue #284 + public bool DisableBom { get; set; } + + /// Builds a new instance of the Rfc5424 class + public Rfc5424() + { + defaultHostname = HostFqdn(); + defaultAppName = Assembly.GetCallingAssembly().GetName().Name; + Version = DefaultVersion; + Hostname = defaultHostname; + AppName = defaultAppName; + ProcId = NilValue; + MsgId = NilValue; + StructuredData = new StructuredData(); + DisableBom = false; + } + + internal override void Initialize(Enforcement enforcement, Facility facility) + { + base.Initialize(enforcement, facility); + hostnamePolicySet = new FqdnHostnamePolicySet(enforcement, defaultHostname); + appNamePolicySet = new AppNamePolicySet(enforcement, defaultAppName); + procIdPolicySet = new ProcIdPolicySet(enforcement); + msgIdPolicySet = new MsgIdPolicySet(enforcement); + msgWithoutPreamblePolicy = new MsgWithoutPreamblePolicy(enforcement); + StructuredData.Initialize(enforcement); + } + + internal override IEnumerable BuildMessage(LogEventInfo logEvent, string pri, string logEntry) + { + var encodings = new EncodingSet(!DisableBom); + + var msgPrefixBytes = HeaderBytes(pri, logEvent, encodings) + .Concat(SpaceBytes) + .Concat(StructuredData.Bytes(logEvent, encodings)) + .Concat(SpaceBytes) + .ToArray(); + var msgBytes = MsgBytes(logEntry, msgPrefixBytes.Length, encodings); + return msgPrefixBytes.Concat(msgBytes); + } + + private static string HostFqdn() + { + var hostname = Dns.GetHostName(); + var domainName = IPGlobalProperties.GetIPGlobalProperties().DomainName; + var domainAsSuffix = $".{domainName}"; + return hostname.EndsWith(domainAsSuffix) ? hostname : $"{hostname}{domainAsSuffix}"; + } + + private IEnumerable HeaderBytes(string pri, LogEventInfo logEvent, EncodingSet encodings) + { + var timestamp = string.Format(CultureInfo.InvariantCulture, TimestampFormat, logEvent.TimeStamp); + var hostname = hostnamePolicySet.Apply(Hostname.Render(logEvent)); + var appName = appNamePolicySet.Apply(AppName.Render(logEvent)); + var procId = procIdPolicySet.Apply(ProcId.Render(logEvent)); + var msgId = msgIdPolicySet.Apply(MsgId.Render(logEvent)); + var header = $"{pri}{Version} {timestamp} {hostname} {appName} {procId} {msgId}"; + return encodings.Ascii.GetBytes(header); + } + + private IEnumerable MsgBytes(string logEntry, int msgPrefixLength, EncodingSet encodings) + { + var preambleBytes = encodings.Utf8.GetPreamble(); + var logEntryBytes = encodings.Utf8.GetBytes(logEntry); + var msgWithoutPreamblePrefixLength = msgPrefixLength + preambleBytes.Length; + var msgWithoutPreambleBytes = msgWithoutPreamblePolicy.Apply(logEntryBytes, msgWithoutPreamblePrefixLength); + return preambleBytes.Concat(msgWithoutPreambleBytes); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/RfcNumber.cs b/src/NLog.Targets.Syslog/MessageCreation/RfcNumber.cs new file mode 100644 index 00000000..a96ccf9d --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/RfcNumber.cs @@ -0,0 +1,9 @@ +namespace NLog.Targets.Syslog.MessageCreation +{ + /// The Syslog protocol RFC to be followed + public enum RfcNumber + { + Rfc3164 = 3164, + Rfc5424 = 5424 + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/SdElement.cs b/src/NLog.Targets.Syslog/MessageCreation/SdElement.cs new file mode 100644 index 00000000..fce788b2 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/SdElement.cs @@ -0,0 +1,74 @@ +using NLog.Config; +using NLog.Targets.Syslog.Policies; +using System.Collections.Generic; +using System.Linq; + +namespace NLog.Targets.Syslog.MessageCreation +{ + /// A Syslog SD-ELEMENT field + [NLogConfigurationItem] + public class SdElement + { + private SdIdToInvalidParamNamePattern sdIdToInvalidParamNamePattern; + private static readonly byte[] LeftBracketBytes = { 0x5B }; + private static readonly byte[] RightBracketBytes = { 0x5D }; + + /// The SD-ID field of an SD-ELEMENT field in the STRUCTURED-DATA part + public SdId SdId { get; set; } + + /// The SD-PARAM fields belonging to an SD-ELEMENT field in the STRUCTURED-DATA part + [ArrayParameter(typeof(SdParam), nameof(SdParam))] + public IList SdParams { get; set; } + + /// Builds a new instance of the SdElement class + public SdElement() + { + sdIdToInvalidParamNamePattern = new SdIdToInvalidParamNamePattern(); + SdParams = new List(); + } + + internal void Initialize(Enforcement enforcement) + { + SdId.Initialize(enforcement); + SdParams.ForEach(sdParam => sdParam.Initialize(enforcement)); + } + + internal static string ToString(IEnumerable sdElements) + { + return sdElements.Aggregate(string.Empty, (acc, curr) => acc.ToString() + curr.ToString()); + } + + public override string ToString() + { + return $"[{SdId}{SdParam.ToString(SdParams)}]"; + } + + internal static IEnumerable Bytes(IEnumerable sdElements, LogEventInfo logEvent, EncodingSet encodings) + { + var elements = sdElements.ToList(); + + var ids = elements.Select(x => x.SdId); + var encodedIds = SdId.Bytes(ids, logEvent, encodings).ToList(); + + var encodedparamLists = elements + .Select(x => + { + var renderedId = x.SdId.Render(logEvent); + var invalidParamNames = SdIdToInvalidParamNamePattern.Map(renderedId); + return SdParam.Bytes(x.SdParams, logEvent, invalidParamNames, encodings); + }) + .ToList(); + + return elements.SelectMany((e, i) => Bytes(encodedIds[i], encodedparamLists[i])); + } + + private static IEnumerable Bytes(IEnumerable sdIdBytes, IEnumerable sdParamsBytes) + { + return LeftBracketBytes + .Concat(LeftBracketBytes) + .Concat(sdIdBytes) + .Concat(sdParamsBytes) + .Concat(RightBracketBytes); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/SdId.cs b/src/NLog.Targets.Syslog/MessageCreation/SdId.cs new file mode 100644 index 00000000..6a2eaf91 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/SdId.cs @@ -0,0 +1,54 @@ +using NLog.Layouts; +using NLog.Targets.Syslog.Policies; +using System.Collections.Generic; +using System.Linq; + +namespace NLog.Targets.Syslog.MessageCreation +{ + public class SdId : SimpleLayout + { + private static readonly InternalLogDuplicatesPolicy DuplicatesPolicy; + private SdIdPolicySet sdIdPolicySet; + + static SdId() + { + DuplicatesPolicy = new InternalLogDuplicatesPolicy(); + } + + public SdId() : this(string.Empty) + { + } + + public SdId(string text) : base(text) + { + } + + internal void Initialize(Enforcement enforcement) + { + sdIdPolicySet = new SdIdPolicySet(enforcement); + } + + public static implicit operator SdId(string text) + { + return new SdId(text); + } + + internal static IEnumerable> Bytes(IEnumerable sdIds, LogEventInfo logEvent, EncodingSet encodings) + { + return InternalLogDuplicatesPolicy.Apply(sdIds, x => x.Render(logEvent)) + .Select(x => x.Bytes(logEvent, encodings)); + } + + private IEnumerable Bytes(LogEventInfo logEvent, EncodingSet encodings) + { + var sdId = sdIdPolicySet.Apply(Render(logEvent)); + return encodings.Ascii.GetBytes(sdId); + } + + public override string ToString() + { + var nullEvent = LogEventInfo.CreateNullEvent(); + return Render(nullEvent); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/SdIdToInvalidParamNamePattern.cs b/src/NLog.Targets.Syslog/MessageCreation/SdIdToInvalidParamNamePattern.cs new file mode 100644 index 00000000..9a0f6533 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/SdIdToInvalidParamNamePattern.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; + +namespace NLog.Targets.Syslog.MessageCreation +{ + internal class SdIdToInvalidParamNamePattern + { + private const string Timequality = "timeQuality"; + private const string Origin = "origin"; + private const string Meta = "meta"; + private static readonly string[] TimeQualityParamNames = { "tzKnown", "isSynced", "syncAccuracy" }; + private static readonly string[] OriginParamNames = { "ip", "enterpriseId", "software", "swVersion" }; + private static readonly string[] MetaParamNames = { "sequenceId", "sysUpTime", "language" }; + private const string InvalidIanaParamName = @"^(?!^(?:{0})$).*$"; + private static readonly Dictionary InvalidIanaParamNames = new Dictionary + { + { Timequality, BuildInvalidIanaParamNamePattern(TimeQualityParamNames) }, + { Origin, BuildInvalidIanaParamNamePattern(OriginParamNames) }, + { Meta, BuildInvalidIanaParamNamePattern(MetaParamNames) } + }; + private const string NonSafePrintUsAscii = @"[^\u0022\u003D\u005D\u0020-\u007E]"; + private const string InvalidCustomParamName = NonSafePrintUsAscii; + + /// Maps an SD-ID to the corresponding invalid values for PARAM-NAME fields + /// The string representation of the SD-ID + /// The invalid PARAM-NAME pattern + public static string Map(string sdId) + { + return InvalidIanaParamNames.ContainsKey(sdId) ? InvalidIanaParamNames[sdId] : InvalidCustomParamName; + } + + private static string BuildInvalidIanaParamNamePattern(params string[] args) + { + var joined = string.Join("|", args); + var formatted = string.Format(InvalidIanaParamName, joined); + return formatted; + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/SdParam.cs b/src/NLog.Targets.Syslog/MessageCreation/SdParam.cs new file mode 100644 index 00000000..3793385a --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/SdParam.cs @@ -0,0 +1,68 @@ +using NLog.Config; +using NLog.Layouts; +using NLog.Targets.Syslog.Policies; +using System.Collections.Generic; +using System.Linq; + +namespace NLog.Targets.Syslog.MessageCreation +{ + /// A Syslog SD-PARAM field + [NLogConfigurationItem] + public class SdParam + { + private ParamNamePolicySet paramNamePolicySet; + private ParamValuePolicySet paramValuePolicySet; + private static readonly byte[] SpaceBytes = { 0x20 }; + private static readonly byte[] EqualBytes = { 0x3D }; + private static readonly byte[] QuotesBytes = { 0x22 }; + + /// The PARAM-NAME field of this SD-PARAM + public Layout Name { get; set; } + + /// The PARAM-VALUE field of this SD-PARAM + public Layout Value { get; set; } + + internal void Initialize(Enforcement enforcement) + { + paramNamePolicySet = new ParamNamePolicySet(enforcement); + paramValuePolicySet = new ParamValuePolicySet(enforcement); + } + + internal static string ToString(IEnumerable sdParams) + { + return sdParams.Aggregate(string.Empty, (acc, cur) => $"{acc} {cur.ToString()}"); + } + + public override string ToString() + { + var nullEvent = LogEventInfo.CreateNullEvent(); + return $"{Name.Render(nullEvent)}=\"{Value.Render(nullEvent)}\""; + } + + internal static IEnumerable Bytes(IEnumerable sdParams, LogEventInfo logEvent, string invalidNamesPattern, EncodingSet encodings) + { + return sdParams.SelectMany(sdParam => SpaceBytes.Concat(sdParam.Bytes(logEvent, invalidNamesPattern, encodings))); + } + + private IEnumerable Bytes(LogEventInfo logEvent, string invalidNamesPattern, EncodingSet encodings) + { + return NameBytes(logEvent, invalidNamesPattern, encodings) + .Concat(EqualBytes) + .Concat(QuotesBytes) + .Concat(ValueBytes(logEvent, encodings)) + .Concat(QuotesBytes); + } + + private IEnumerable NameBytes(LogEventInfo logEvent, string invalidNamesPattern, EncodingSet encodings) + { + var paramName = paramNamePolicySet.Apply(Name.Render(logEvent), invalidNamesPattern); + return encodings.Ascii.GetBytes(paramName); + } + + private IEnumerable ValueBytes(LogEventInfo logEvent, EncodingSet encodings) + { + var paramValue = paramValuePolicySet.Apply(Value.Render(logEvent)); + return encodings.Utf8.GetBytes(paramValue); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/Severity.cs b/src/NLog.Targets.Syslog/MessageCreation/Severity.cs new file mode 100644 index 00000000..5fecfaa0 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/Severity.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; + +namespace NLog.Targets.Syslog.MessageCreation +{ + internal class Severity + { + private readonly int value; + private static readonly Severity Emergency = new Severity(0); + //private static readonly Severity Alert = new Severity(1); + //private static readonly Severity Critical = new Severity(2); + private static readonly Severity Error = new Severity(3); + private static readonly Severity Warning = new Severity(4); + private static readonly Severity Notice = new Severity(5); + private static readonly Severity Informational = new Severity(6); + private static readonly Severity Debug = new Severity(7); + private static readonly Dictionary LogLevelToSeverity; + + static Severity() + { + LogLevelToSeverity = new Dictionary + { + { LogLevel.Fatal, Emergency }, + { LogLevel.Error, Error }, + { LogLevel.Warn, Warning }, + { LogLevel.Info, Informational }, + { LogLevel.Debug, Debug }, + { LogLevel.Trace, Notice } + }; + } + + private Severity(int value) + { + this.value = value; + } + + public static explicit operator int(Severity severity) + { + return severity.value; + } + + public static explicit operator Severity(LogLevel logLevel) + { + return LogLevelToSeverity[logLevel]; + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageCreation/StructuredData.cs b/src/NLog.Targets.Syslog/MessageCreation/StructuredData.cs new file mode 100644 index 00000000..e3e34e6b --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageCreation/StructuredData.cs @@ -0,0 +1,47 @@ +using NLog.Config; +using NLog.Layouts; +using NLog.Targets.Syslog.Policies; +using System.Collections.Generic; + +namespace NLog.Targets.Syslog.MessageCreation +{ + /// A Syslog STRUCTURED-DATA part + [NLogConfigurationItem] + public class StructuredData + { + private const string NilValue = "-"; + private static readonly byte[] NilValueBytes = { 0x2D }; + + public Layout FromEventProperties { get; set; } + + /// The SD-ELEMENTs contained in the STRUCTURED-DATA part + [ArrayParameter(typeof(SdElement), nameof(SdElement))] + public IList SdElements { get; set; } + + /// Builds a new instance of the StructuredData class + public StructuredData() + { + FromEventProperties = string.Empty; + SdElements = new List(); + } + + internal void Initialize(Enforcement enforcement) + { + SdElements.ForEach(sdElem => sdElem.Initialize(enforcement)); + } + + public override string ToString() + { + return SdElements.Count == 0 ? NilValue : SdElement.ToString(SdElements); + } + + internal IEnumerable Bytes(LogEventInfo logEvent, EncodingSet encodings) + { + var sdFromEvtProps = FromEventProperties.Render(logEvent); + if (!string.IsNullOrEmpty(sdFromEvtProps)) + return encodings.Utf8.GetBytes(sdFromEvtProps); + + return SdElements.Count == 0 ? NilValueBytes : SdElement.Bytes(SdElements, logEvent, encodings); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageSend/FramingMethod.cs b/src/NLog.Targets.Syslog/MessageSend/FramingMethod.cs new file mode 100644 index 00000000..54f7f810 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageSend/FramingMethod.cs @@ -0,0 +1,9 @@ +namespace NLog.Targets.Syslog.MessageSend +{ + /// The framing method to be used when transmitting a message + public enum FramingMethod + { + NonTransparent, + OctetCounting + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageSend/MessageTransmitter.cs b/src/NLog.Targets.Syslog/MessageSend/MessageTransmitter.cs new file mode 100644 index 00000000..215f8ec6 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageSend/MessageTransmitter.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; + +namespace NLog.Targets.Syslog.MessageSend +{ + public abstract class MessageTransmitter + { + private const string Localhost = "localhost"; + private const int DefaultPort = 514; + + /// The IP address of the Syslog server or an empty string + protected string IpAddress { get; private set; } + + private string server; + + /// The IP address or hostname of the Syslog server + public string Server + { + get { return server; } + set { server = value; IpAddress = Dns.GetHostAddresses(Server).FirstOrDefault()?.ToString(); } + } + + /// The port number the Syslog server is listening on + public int Port { get; set; } + + /// Builds the base part of a new instance of a class inheriting from MessageTransmitter + protected MessageTransmitter() + { + Server = Localhost; + Port = DefaultPort; + } + + internal virtual IEnumerable FrameMessageOrLeaveItUnchanged(IEnumerable message) + { + return message; + } + + internal abstract void SendMessages(IEnumerable messages); + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageSend/MessageTransmittersFacade.cs b/src/NLog.Targets.Syslog/MessageSend/MessageTransmittersFacade.cs new file mode 100644 index 00000000..728fede3 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageSend/MessageTransmittersFacade.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; + +namespace NLog.Targets.Syslog.MessageSend +{ + public class MessageTransmittersFacade + { + private MessageTransmitter activeTransmitter; + private readonly Dictionary transmitters; + + /// The Syslog server protocol + public ProtocolType Protocol { get; set; } + + /// UDP related fields + public UdpProtocol UdpProtocol { get; set; } + + /// TCP related fields + public TcpProtocol TcpProtocol { get; set; } + + /// Builds a new instance of the MessageTransmittersFacade class + public MessageTransmittersFacade() + { + Protocol = ProtocolType.Tcp; + UdpProtocol = new UdpProtocol(); + TcpProtocol = new TcpProtocol(); + transmitters = new Dictionary + { + {ProtocolType.Udp, UdpProtocol}, + {ProtocolType.Tcp, TcpProtocol} + }; + } + + internal void Initialize() + { + activeTransmitter = transmitters[Protocol]; + } + + internal IEnumerable FrameMessageOrLeaveItUnchanged(IEnumerable message) + { + return activeTransmitter.FrameMessageOrLeaveItUnchanged(message); + } + + internal void SendMessages(IEnumerable messages) + { + activeTransmitter.SendMessages(messages); + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageSend/ProtocolType.cs b/src/NLog.Targets.Syslog/MessageSend/ProtocolType.cs new file mode 100644 index 00000000..3db84fa8 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageSend/ProtocolType.cs @@ -0,0 +1,9 @@ +namespace NLog.Targets.Syslog.MessageSend +{ + /// The protocol to be used when transmitting a message + public enum ProtocolType + { + Udp, + Tcp + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageSend/TcpProtocol.cs b/src/NLog.Targets.Syslog/MessageSend/TcpProtocol.cs new file mode 100644 index 00000000..2aff15f7 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageSend/TcpProtocol.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Authentication; +using System.Text; + +namespace NLog.Targets.Syslog.MessageSend +{ + [DisplayName("Tcp")] + public class TcpProtocol : MessageTransmitter + { + private FramingMethod framing; + private static readonly byte[] LineFeedBytes = { 0x0A }; + + /// Whether to use TLS or not (TLS 1.2 only) + public bool UseTls { get; set; } + + /// Which framing method to use + /// If is true get will always return OctetCounting (RFC 5425) + public FramingMethod Framing + { + get { return UseTls ? FramingMethod.OctetCounting : framing; } + set { framing = value; } + } + + /// Builds a new instance of the TcpProtocol class + public TcpProtocol() + { + UseTls = true; + Framing = FramingMethod.OctetCounting; + } + + internal override IEnumerable FrameMessageOrLeaveItUnchanged(IEnumerable message) + { + return OctectCountingFramedOrUnchanged(NonTransparentFramedOrUnchanged(message)); + } + + internal override void SendMessages(IEnumerable messages) + { + if (string.IsNullOrEmpty(IpAddress)) + return; + + using (var tcp = new TcpClient(IpAddress, Port)) + using (var stream = SslDecorate(tcp)) + { + foreach (var message in messages) + stream.Write(message, 0, message.Length); + } + } + + private IEnumerable OctectCountingFramedOrUnchanged(IEnumerable message) + { + if (Framing != FramingMethod.OctetCounting) + return message; + + var messageAsArray = message.ToArray(); + var octetCount = messageAsArray.Length; + var prefix = new ASCIIEncoding().GetBytes($"{octetCount} "); + return prefix.Concat(messageAsArray); + } + + private IEnumerable NonTransparentFramedOrUnchanged(IEnumerable message) + { + return Framing != FramingMethod.NonTransparent ? message : message.Concat(LineFeedBytes); + } + + private Stream SslDecorate(TcpClient tcp) + { + var tcpStream = tcp.GetStream(); + + if (!UseTls) + return tcpStream; + + var sslStream = new SslStream(tcpStream, true); + sslStream.AuthenticateAsClient(Server, null, SslProtocols.Tls12, false); + return sslStream; + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/MessageSend/UdpProtocol.cs b/src/NLog.Targets.Syslog/MessageSend/UdpProtocol.cs new file mode 100644 index 00000000..653e8af0 --- /dev/null +++ b/src/NLog.Targets.Syslog/MessageSend/UdpProtocol.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Net.Sockets; + +namespace NLog.Targets.Syslog.MessageSend +{ + [DisplayName("Udp")] + public class UdpProtocol : MessageTransmitter + { + internal override void SendMessages(IEnumerable messages) + { + if (string.IsNullOrEmpty(IpAddress)) + return; + + using (var udp = new UdpClient(IpAddress, Port)) + { + foreach (var message in messages) + udp.Send(message, message.Length); + } + } + } +} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/NLog.Targets.Syslog.cs b/src/NLog.Targets.Syslog/NLog.Targets.Syslog.cs deleted file mode 100644 index eef9ee78..00000000 --- a/src/NLog.Targets.Syslog/NLog.Targets.Syslog.cs +++ /dev/null @@ -1,287 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// NLog.Targets.Syslog -// ------------------------------------------------------------------------ -// Copyright 2013 Jesper Hess Nielsen -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//////////////////////////////////////////////////////////////////////////////// - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using NLog.Common; -using NLog.Layouts; - -// ReSharper disable UnusedMember.Global -// ReSharper disable MemberCanBePrivate.Global -// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global -// ReSharper disable UnusedAutoPropertyAccessor.Global -// ReSharper disable CheckNamespace -namespace NLog.Targets -// ReSharper restore CheckNamespace -{ - /// This class enables logging to a unix-style syslog server using NLog - [Target("Syslog")] - public class Syslog : TargetWithLayout - { - private const string NilValue = "-"; - private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private static readonly byte[] _bom = { 0xEF, 0xBB, 0xBF }; - private static readonly char[] _lineSeps = { '\r', '\n' }; - - /// Gets or sets the IP Address or Host name of your Syslog server - public string SyslogServer { get; set; } - - /// Gets or sets the port number syslog is running on (usually 514) - public int Port { get; set; } - - /// Gets or sets the name of the application that will show up in the syslog log - public Layout Sender { get; set; } - - /// Gets or sets the timestamp format - public string TimestampFormat { get; set; } - - /// Gets or sets the machine name hosting syslog - public Layout MachineName { get; set; } - - /// Gets or sets the syslog facility name to transmit message from (e.g. local0 or local7) - public SyslogFacility Facility { get; set; } - - /// Gets or sets the syslog server protocol (TCP/UDP) - public ProtocolType Protocol { get; set; } - - /// If this is set, try to configure and use SSL if available - public bool Ssl { get; set; } - - /// If set, split message by newlines and send as separate messages - public bool SplitNewlines { get; set; } - - /// RFC number for syslog protocol - public RfcNumber Rfc { get; set; } - - #region RFC 5424 members - - /// Syslog protocol version for RFC 5424 - private byte ProtocolVersion { get; } - - /// Layout for PROCID protocol field - public Layout ProcId { get; set; } - - /// Layout for MSGID protocol field - public Layout MsgId { get; set; } - - /// Layout for STRUCTURED-DATA protocol field - public Layout StructuredData { get; set; } - - #endregion - - /// Initializes a new instance of the Syslog class - public Syslog() - { - SyslogServer = "127.0.0.1"; - Port = 514; - Sender = Assembly.GetCallingAssembly().GetName().Name; - Facility = SyslogFacility.Local1; - Protocol = ProtocolType.Udp; - TimestampFormat = "MMM dd HH:mm:ss"; - MachineName = Dns.GetHostName(); - SplitNewlines = true; - Rfc = RfcNumber.Rfc3164; - - //Defaults for rfc 5424 - ProtocolVersion = 1; - ProcId = NilValue; - MsgId = NilValue; - StructuredData = NilValue; - } - - /// - /// Writes single event. - /// No need to override sync version of Write(LogEventInfo) because it is called only from async version. - /// - /// The NLog.AsyncLogEventInfo - protected override void Write(AsyncLogEventInfo logEvent) - { - SendEventsBatch(logEvent); - } - - /// Writes array of events - /// The array of NLog.AsyncLogEventInfo - protected override void Write(AsyncLogEventInfo[] logEvents) - { - SendEventsBatch(logEvents); - } - - /// Sends array of events to syslog server - /// The array of NLog.AsyncLogEventInfo - private void SendEventsBatch(params AsyncLogEventInfo[] logEvents) - { - var logServerIp = Dns.GetHostAddresses(SyslogServer).FirstOrDefault(); - if (logServerIp == null) - { - return; - } - var ipAddress = logServerIp.ToString(); - switch (Protocol) - { - case ProtocolType.Udp: - using (var udp = new UdpClient(ipAddress, Port)) - { - ProcessAndSendEvents(logEvents, messageData => udp.Send(messageData, messageData.Length)); - } - break; - case ProtocolType.Tcp: - using (var tcp = new TcpClient(ipAddress, Port)) - { - // disposition of tcp also disposes stream - var stream = tcp.GetStream(); - if (Ssl) - { - // leave stream open so that we don't double dispose - using (var sslStream = new SslStream(stream, true)) - { - sslStream.AuthenticateAsClient(SyslogServer); - ProcessAndSendEvents(logEvents, messageData => sslStream.Write(messageData, 0, messageData.Length)); - } - } - else - { - ProcessAndSendEvents(logEvents, messageData => stream.Write(messageData, 0, messageData.Length)); - } - } - break; - default: - throw new NLogConfigurationException($"Protocol '{Protocol}' is not supported."); - } - } - - /// Processes array of events and sends messages bytes using action - /// The array of NLog.AsyncLogEventInfo - /// Implementation of send data method - void ProcessAndSendEvents(AsyncLogEventInfo[] logEvents, Action messageSendAction) - { - foreach (var asyncLogEvent in logEvents) - { - var logEvent = asyncLogEvent.LogEvent; - var formattedMessageLines = FormatMessageLines(logEvent); - var severity = (SyslogSeverity)logEvent.Level; - foreach (var formattedMessageLine in formattedMessageLines) - { - var message = BuildSyslogMessage(logEvent, Facility, severity, formattedMessageLine); - messageSendAction(message); - } - } - } - - /// Builds a syslog-compatible message using the information we have available - /// The NLog.LogEventInfo - /// Syslog Facility to transmit message from - /// Syslog severity level - /// Message text - /// Byte array containing formatted syslog message - private byte[] BuildSyslogMessage(LogEventInfo logEvent, SyslogFacility facility, SyslogSeverity priority, string body) - { - switch (Rfc) - { - case RfcNumber.Rfc5424: - return BuildSyslogMessage5424(logEvent, facility, priority, body); - default: - return BuildSyslogMessage3164(logEvent, facility, priority, body); - } - } - - /// Builds rfc-3164 compatible message - /// The NLog.LogEventInfo - /// Syslog Facility to transmit message from - /// Syslog severity level - /// Message text - /// Byte array containing formatted syslog message - private byte[] BuildSyslogMessage3164(LogEventInfo logEvent, SyslogFacility facility, SyslogSeverity severity, string body) - { - // Calculate PRI field - var priority = CalculatePriorityValue(facility, severity).ToString(CultureInfo.InvariantCulture); - var time = logEvent.TimeStamp.ToLocalTime().ToString(TimestampFormat, _usCulture); - // Get sender machine name - var machine = MachineName.Render(logEvent); - // Get sender - var sender = Sender.Render(logEvent); - - return Encoding.ASCII.GetBytes($"<{priority}>{time} {machine} {sender}: {body}{Environment.NewLine}"); - } - - /// Builds rfc-5424 compatible message - /// The NLog.LogEventInfo - /// Syslog Facility to transmit message from - /// Syslog severity level - /// Message text - /// Byte array containing formatted syslog message - private byte[] BuildSyslogMessage5424(LogEventInfo logEvent, SyslogFacility facility, SyslogSeverity severity, string body) - { - // Calculate PRI field - var priority = CalculatePriorityValue(facility, severity).ToString(CultureInfo.InvariantCulture); - var version = ProtocolVersion.ToString(CultureInfo.InvariantCulture); - var time = logEvent.TimeStamp.ToString("o"); - // Get sender machine name - var machine = Left(MachineName.Render(logEvent), 255); - var sender = Left(Sender.Render(logEvent), 48); - var procId = Left(ProcId.Render(logEvent), 128); - var msgId = Left(MsgId.Render(logEvent), 32); - - var headerData = Encoding.ASCII.GetBytes($"<{priority}>{version} {time} {machine} {sender} {procId} {msgId} "); - var structuredData = Encoding.UTF8.GetBytes(StructuredData.Render(logEvent) + " "); - var messageData = Encoding.UTF8.GetBytes(body); - - var allData = new List(headerData.Length + structuredData.Length + _bom.Length + messageData.Length); - allData.AddRange(headerData); - allData.AddRange(structuredData); - allData.AddRange(_bom); - allData.AddRange(messageData); - return allData.ToArray(); - } - - /// Gets at most length first symbols - /// Source string - /// Maximum symbols count - /// String that contains at most length symbols - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string Left(string value, int length) - { - return value.Length <= length ? value : value.Substring(0, length); - } - - /// Renders message lines - /// The NLog.LogEventInfo - private IEnumerable FormatMessageLines(LogEventInfo logEvent) - { - var msg = Layout.Render(logEvent); - return SplitNewlines ? msg.Split(_lineSeps, StringSplitOptions.RemoveEmptyEntries) : new[] { msg }; - } - - /// Calculates syslog PRIVAL - /// Syslog facility to transmit message from - /// Syslog severity level - /// Byte array containing formatted syslog message - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int CalculatePriorityValue(SyslogFacility facility, SyslogSeverity severity) - { - return (int)facility * 8 + (int)severity; - } - } -} \ No newline at end of file diff --git a/src/NLog.Targets.Syslog/NLog.Targets.Syslog.csproj b/src/NLog.Targets.Syslog/NLog.Targets.Syslog.csproj index 14e7640f..5a22f89e 100644 --- a/src/NLog.Targets.Syslog/NLog.Targets.Syslog.csproj +++ b/src/NLog.Targets.Syslog/NLog.Targets.Syslog.csproj @@ -43,23 +43,68 @@ - ..\packages\NLog.4.2.2\lib\net45\NLog.dll + ..\packages\NLog.4.3.3\lib\net45\NLog.dll True - + + ..\packages\UnidecodeSharpFork.1.0.0\lib\UnidecodeSharpFork.dll + True + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + +--> - - - - - - - - - - - - - - - - - - - - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:sl="https://github.com/graffen/NLog.Targets.Syslog/tree/master/src/schemas/NLog.Targets.Syslog.xsd"> + + + + + + utf-8 + true + + ${basedir}/log.txt + + + + + + true + true + true + true + 1024 + + + Local4 + Rfc3164 + + + + TCP + + 127.0.0.1 + 1514 + + + 127.0.0.1 + 1514 + false + octetCounting + + + + + + + false + false + false + true + 1024 + + + Local4 + Rfc5424 + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + UDP + + 127.0.0.1 + 1514 + + + 127.0.0.1 + 1514 + false + nonTransparent + + + + + + + false + false + false + true + 1024 + + + Local4 + Rfc5424 + + + + + + + true + + + + UDP + + 127.0.0.1 + 1514 + + + 127.0.0.1 + 1514 + false + octetCounting + + + + + + + + + + + \ No newline at end of file diff --git a/src/TestApp/NLog.xsd b/src/TestApp/NLog.xsd new file mode 100644 index 00000000..dc821bc0 --- /dev/null +++ b/src/TestApp/NLog.xsd @@ -0,0 +1,2601 @@ + + + + + + + + + + + + + + + Watch config file for changes and reload automatically. + + + + + Print internal NLog messages to the console. Default value is: false + + + + + Print internal NLog messages to the console error output. Default value is: false + + + + + Write internal NLog messages to the specified file. + + + + + Log level threshold for internal log messages. Default value is: Info. + + + + + Global log level threshold for application log messages. Messages below this level won't be logged.. + + + + + Pass NLog internal exceptions to the application. Default value is: false. + + + + + Write internal NLog messages to the the System.Diagnostics.Trace. Default value is: false + + + + + + + + + + + + + + Make all targets within this section asynchronous (creates additional threads but the calling thread isn't blocked by any target writes). + + + + + + + + + + + + + + + + + Prefix for targets/layout renderers/filters/conditions loaded from this assembly. + + + + + Load NLog extensions from the specified file (*.dll) + + + + + Load NLog extensions from the specified assembly. Assembly name should be fully qualified. + + + + + + + + + + Name of the logger. May include '*' character which acts like a wildcard. Allowed forms are: *, Name, *Name, Name* and *Name* + + + + + Comma separated list of levels that this rule matches. + + + + + Minimum level that this rule matches. + + + + + Maximum level that this rule matches. + + + + + Level that this rule matches. + + + + + Comma separated list of target names. + + + + + Ignore further rules if this one matches. + + + + + Enable or disable logging rule. Disabled rules are ignored. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the file to be included. The name is relative to the name of the current config file. + + + + + Ignore any errors in the include file. + + + + + + + Variable name. + + + + + Variable value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Indicates whether to add <!-- --> comments around all written texts. + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events that should be processed in a batch by the lazy writer thread. + + + + + Action to be taken when the lazy writer thread request queue count exceeds the set limit. + + + + + Limit on the number of requests in the lazy writer thread request queue. + + + + + Time in milliseconds to sleep between batches. + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of log events to be buffered. + + + + + Timeout (in milliseconds) after which the contents of buffer will be flushed if there's no write in the specified period of time. Use -1 to disable timed flushes. + + + + + Indicates whether to use sliding timeout. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Indicates whether to keep connection open whenever possible. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Maximum current connections. 0 = no maximum. + + + + + Network address. + + + + + Maximum queue size. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + NDC item separator. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Indicates whether to include dictionary contents. + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout that should be use to calcuate the value for the parameter. + + + + + Viewer parameter name. + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to use default row highlighting rules. + + + + + The encoding for writing messages to the . + + + + + Indicates whether the error stream (stderr) should be used instead of the output stream (stdout). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Condition that must be met in order to set the specified foreground and background color. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + Indicates whether to ignore case when comparing texts. + + + + + Regular expression to be matched. You must specify either text or regex. + + + + + Text to be matched. You must specify either text or regex. + + + + + Indicates whether to match whole words only. + + + + + Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + + + + + Background color. + + + + + Foreground color. + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to send the log messages to the standard error instead of the standard output. + + + + + The encoding for writing messages to the . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Connection string. When provided, it overrides the values specified in DBHost, DBUserName, DBPassword, DBDatabase. + + + + + Name of the connection string (as specified in <connectionStrings> configuration section. + + + + + Database name. If the ConnectionString is not provided this value will be used to construct the "Database=" part of the connection string. + + + + + Database host name. If the ConnectionString is not provided this value will be used to construct the "Server=" part of the connection string. + + + + + Database password. If the ConnectionString is not provided this value will be used to construct the "Password=" part of the connection string. + + + + + Name of the database provider. + + + + + Database user name. If the ConnectionString is not provided this value will be used to construct the "User ID=" part of the connection string. + + + + + Indicates whether to keep the database connection open between the log events. + + + + + Obsolete - value will be ignored! The logging code always runs outside of transaction. Gets or sets a value indicating whether to use database transactions. Some data providers require this. + + + + + Connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. + + + + + Text of the SQL command to be run on each log level. + + + + + Type of the SQL command to be run on each log level. + + + + + + + + + + + + + + + + + + + + + + + Type of the command. + + + + + Connection string to run the command against. If not provided, connection string from the target is used. + + + + + Indicates whether to ignore failures. + + + + + Command text. + + + + + + + + + + + + + + Layout that should be use to calcuate the value for the parameter. + + + + + Database parameter name. + + + + + Database parameter precision. + + + + + Database parameter scale. + + + + + Database parameter size. + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Layout that renders event Category. + + + + + Layout that renders event ID. + + + + + Name of the Event Log to write to. This can be System, Application or any user-defined name. + + + + + Name of the machine on which Event Log service is running. + + + + + Value to be used as the event Source. + + + + + Action to take if the message is larger than the option. + + + + + Optional entrytype. When not set, or when not convertable to then determined by + + + + + Message length limit to write to the Event Log. + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether to return to the first target after any successful write. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + File encoding. + + + + + Line ending mode. + + + + + Way file archives are numbered. + + + + + Name of the file to be used for an archive. + + + + + Indicates whether to automatically archive log files every time the specified time passes. + + + + + Size in bytes above which log files will be automatically archived. Warning: combining this with isn't supported. We cannot create multiple archive files, if they should have the same name. Choose: + + + + + Maximum number of archive files that should be kept. + + + + + Indicates whether to compress archive files into the zip archive format. + + + + + Gets or set a value indicating whether a managed file stream is forced, instead of used the native implementation. + + + + + Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. If set to false, nothing gets written when the filename is wrong. + + + + + Name of the file to write to. + + + + + Value specifying the date format to use when archiving files. + + + + + Indicates whether to archive old log file on startup. + + + + + Indicates whether to create directories if they do not exist. + + + + + Indicates whether to enable log file(s) to be deleted. + + + + + File attributes (Windows only). + + + + + Indicates whether to delete old log file on startup. + + + + + Indicates whether to replace file contents on each write instead of appending log message at the end. + + + + + Indicates whether concurrent writes to the log file by multiple processes on the same host. + + + + + Delay in milliseconds to wait before attempting to write to the file again. + + + + + Maximum number of log filenames that should be stored as existing. + + + + + Indicates whether concurrent writes to the log file by multiple processes on different network hosts. + + + + + Number of files to be kept open. Setting this to a higher value may improve performance in a situation where a single File target is writing to many files (such as splitting by level or by logger). + + + + + Maximum number of seconds that files are kept open. If this number is negative the files are not automatically closed after a period of inactivity. + + + + + Log file buffer size in bytes. + + + + + Indicates whether to automatically flush the file buffers after each log message. + + + + + Number of times the write is appended on the file before NLog discards the log message. + + + + + Indicates whether to keep log file open instead of opening and closing it on each logging event. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Condition expression. Log events who meet this condition will be forwarded to the wrapped target. + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Windows domain name to change context to. + + + + + Required impersonation level. + + + + + Type of the logon provider. + + + + + Logon Type. + + + + + User account password. + + + + + Indicates whether to revert to the credentials of the process instead of impersonating another user. + + + + + Username to change context to. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Endpoint address. + + + + + Name of the endpoint configuration in WCF configuration file. + + + + + Indicates whether to use a WCF service contract that is one way (fire and forget) or two way (request-reply) + + + + + Client ID. + + + + + Indicates whether to include per-event properties in the payload sent to the server. + + + + + Indicates whether to use binary message encoding. + + + + + + + + + + + + + + Layout that should be use to calculate the value for the parameter. + + + + + Name of the parameter. + + + + + Type of the parameter. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Text to be rendered. + + + + + Header. + + + + + Footer. + + + + + Indicates whether to send message as HTML instead of plain text. + + + + + Encoding to be used for sending e-mail. + + + + + Indicates whether to add new lines between log entries. + + + + + CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). + + + + + Mail message body (repeated for each log message send in one mail). + + + + + Mail subject. + + + + + Sender's email address (e.g. joe@domain.com). + + + + + Indicates whether NewLine characters in the body should be replaced with tags. + + + + + Priority used for sending mails. + + + + + Indicates the SMTP client timeout. + + + + + SMTP Server to be used for sending. + + + + + SMTP Authentication mode. + + + + + Username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). + + + + + Indicates whether SSL (secure sockets layer) should be used when communicating with SMTP server. + + + + + Port number that SMTP Server is listening on. + + + + + Indicates whether the default Settings from System.Net.MailSettings should be used. + + + + + Folder where applications save mail messages to be processed by the local SMTP server. + + + + + Specifies how outgoing email messages will be handled. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Encoding to be used when writing text to the queue. + + + + + Indicates whether to use the XML format when serializing message. This will also disable creating queues. + + + + + Indicates whether to check if a queue exists before writing to it. + + + + + Indicates whether to create the queue if it doesn't exists. + + + + + Label to associate with each message. + + + + + Name of the queue to write to. + + + + + Indicates whether to use recoverable messages (with guaranteed delivery). + + + + + + + + + + + + + + + + + Name of the target. + + + + + Class name. + + + + + Method name. The method must be public and static. Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx e.g. + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Encoding to be used. + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Network address. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Indicates whether to keep connection open whenever possible. + + + + + Maximum current connections. 0 = no maximum. + + + + + Maximum queue size. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Encoding to be used. + + + + + Instance of that is used to format log messages. + + + + + Maximum message size in bytes. + + + + + Indicates whether to append newline at the end of log message. + + + + + Action that should be taken if the will be more connections than . + + + + + Action that should be taken if the message is larger than maxMessageSize. + + + + + Indicates whether to keep connection open whenever possible. + + + + + Size of the connection cache (number of connections which are kept alive). + + + + + Maximum current connections. 0 = no maximum. + + + + + Network address. + + + + + Maximum queue size. + + + + + Indicates whether to include source info (file name and line number) in the information sent over the network. + + + + + NDC item separator. + + + + + Indicates whether to include stack contents. + + + + + Indicates whether to include call site (class and method name) in the information sent over the network. + + + + + AppInfo field. By default it's the friendly name of the current AppDomain. + + + + + Indicates whether to include NLog-specific extensions to log4j schema. + + + + + Indicates whether to include dictionary contents. + + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + Indicates whether to perform layout calculation. + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Indicates whether performance counter should be automatically created. + + + + + Name of the performance counter category. + + + + + Counter help text. + + + + + Name of the performance counter. + + + + + Performance counter type. + + + + + The value by which to increment the counter. + + + + + Performance counter instance name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Default filter to be applied when no specific rule matches. + + + + + + + + + + + + + Condition to be tested. + + + + + Resulting filter to be applied when the condition matches. + + + + + + + + + + + + Name of the target. + + + + + + + + + + + + + + + Name of the target. + + + + + Number of times to repeat each log message. + + + + + + + + + + + + + + + + Name of the target. + + + + + Number of retries that should be attempted on the wrapped target in case of a failure. + + + + + Time to wait between retries in milliseconds. + + + + + + + + + + + + + + Name of the target. + + + + + + + + + + + + + + Name of the target. + + + + + + + + + + + + + + + Name of the target. + + + + + Layout used to format log messages. + + + + + + + + + + + + + + + + + + + + + Name of the target. + + + + + Should we include the BOM (Byte-order-mark) for UTF? Influences the property. This will only work for UTF-8. + + + + + Encoding. + + + + + Web service method name. Only used with Soap. + + + + + Web service namespace. Only used with Soap. + + + + + Protocol to be used when calling web service. + + + + + Web service URL. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + Custom column delimiter value (valid when ColumnDelimiter is set to 'Custom'). + + + + + Column delimiter. + + + + + Quote Character. + + + + + Quoting mode. + + + + + Indicates whether CVS should include header. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Layout of the column. + + + + + Name of the column. + + + + + + + + + + + + + Option to suppress the extra spaces in the output json + + + + + + + + + + + + + + Determines wether or not this attribute will be Json encoded. + + + + + Layout that will be rendered as the attribute's value. + + + + + Name of the attribute. + + + + + + + + + + + + + + Footer layout. + + + + + Header layout. + + + + + Body layout (can be repeated multiple times). + + + + + + + + + + + + + + + + + + + + + Layout text. + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Condition expression. + + + + + + + + + + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + Substring to be matched. + + + + + + + + + + + + + + + + + Action to be taken when filter matches. + + + + + String to compare the layout to. + + + + + Indicates whether to ignore case when comparing strings. + + + + + Layout to be used to filter log messages. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TestApp/Program.cs b/src/TestApp/Program.cs index bd23040c..e196f60b 100644 --- a/src/TestApp/Program.cs +++ b/src/TestApp/Program.cs @@ -5,15 +5,12 @@ namespace TestApp { public static class Program { - /// - /// The main entry point for the application. - /// [STAThread] public static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new Form1()); + Application.Run(new FormTest()); } } } \ No newline at end of file diff --git a/src/TestApp/TestApp.csproj b/src/TestApp/TestApp.csproj index 34b44349..66ac93e4 100644 --- a/src/TestApp/TestApp.csproj +++ b/src/TestApp/TestApp.csproj @@ -17,8 +17,8 @@ ..\ true - - x86 + + AnyCPU true full false @@ -26,44 +26,36 @@ DEBUG;TRACE prompt 4 - false - - x86 + + AnyCPU pdbonly true bin\Release\ TRACE prompt 4 - false - ..\packages\NLog.4.2.2\lib\net45\NLog.dll + ..\packages\NLog.4.3.3\lib\net45\NLog.dll True - - - - - - - + Form - - Form1.cs + + FormTest.cs - - Form1.cs + + FormTest.cs ResXFileCodeGenerator @@ -80,7 +72,12 @@ Always Designer - + + Designer + + + Designer + SettingsSingleFileGenerator Settings.Designer.cs diff --git a/src/TestApp/app.config b/src/TestApp/app.config index 51278a45..17291947 100644 --- a/src/TestApp/app.config +++ b/src/TestApp/app.config @@ -1,3 +1,6 @@ - + + + + diff --git a/src/TestApp/packages.config b/src/TestApp/packages.config index d58c5f7c..32468a73 100644 --- a/src/TestApp/packages.config +++ b/src/TestApp/packages.config @@ -1,4 +1,5 @@  - + + \ No newline at end of file diff --git a/src/schemas/NLog.Targets.Syslog.xsd b/src/schemas/NLog.Targets.Syslog.xsd new file mode 100644 index 00000000..839ae81c --- /dev/null +++ b/src/schemas/NLog.Targets.Syslog.xsd @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file