Skip to content

Commit 6ef1ae5

Browse files
authored
Memory leak #81 fix (#82)
* Fix async possible issues * Some cleanup * Bumped version of NLog, switched to Async task * Updated readme * Added WebSample * Switched to appsetting.json to demonstrate json settings
1 parent cb3057f commit 6ef1ae5

20 files changed

+615
-254
lines changed

.dockerignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
**/.classpath
2+
**/.dockerignore
3+
**/.env
4+
**/.git
5+
**/.gitignore
6+
**/.project
7+
**/.settings
8+
**/.toolstarget
9+
**/.vs
10+
**/.vscode
11+
**/*.*proj.user
12+
**/*.dbmdl
13+
**/*.jfm
14+
**/azds.yaml
15+
**/bin
16+
**/charts
17+
**/docker-compose*
18+
**/Dockerfile*
19+
**/node_modules
20+
**/npm-debug.log
21+
**/obj
22+
**/secrets.dev.yaml
23+
**/values.dev.yaml
24+
LICENSE
25+
README.md
26+
!**/.gitignore
27+
!.git/HEAD
28+
!.git/config
29+
!.git/packed-refs
30+
!.git/refs/heads/**

.github/workflows/publish.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ jobs:
1919
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
2020
with:
2121
dotnet-version: ${{ vars.DOTNET_VERSION }}
22-
work-dir: './src/Nlog.RabbitMQ.Target'
22+
work-dir: "./src/Nlog.RabbitMQ.Target"
23+
signing-key: "Nlog.RabbitMQ.Target.snk"

Nlog.RabbitMQ.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nlog.RabbitMQ.Sample", "src
2626
EndProject
2727
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
2828
EndProject
29+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nlog.RabbitMQ.WebSample", "src\Nlog.RabbitMQ.WebSample\Nlog.RabbitMQ.WebSample.csproj", "{7E369F8C-967F-4B9B-AF00-A9C486748272}"
30+
EndProject
2931
Global
3032
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3133
Debug|Any CPU = Debug|Any CPU
@@ -44,6 +46,10 @@ Global
4446
{6CD9BEEC-158D-4CFE-B24F-A3E328782777}.Debug|Any CPU.Build.0 = Debug|Any CPU
4547
{6CD9BEEC-158D-4CFE-B24F-A3E328782777}.Release|Any CPU.ActiveCfg = Release|Any CPU
4648
{6CD9BEEC-158D-4CFE-B24F-A3E328782777}.Release|Any CPU.Build.0 = Release|Any CPU
49+
{7E369F8C-967F-4B9B-AF00-A9C486748272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50+
{7E369F8C-967F-4B9B-AF00-A9C486748272}.Debug|Any CPU.Build.0 = Debug|Any CPU
51+
{7E369F8C-967F-4B9B-AF00-A9C486748272}.Release|Any CPU.ActiveCfg = Release|Any CPU
52+
{7E369F8C-967F-4B9B-AF00-A9C486748272}.Release|Any CPU.Build.0 = Release|Any CPU
4753
EndGlobalSection
4854
GlobalSection(SolutionProperties) = preSolution
4955
HideSolutionNode = FALSE
@@ -53,6 +59,7 @@ Global
5359
{36A98B2A-610E-4034-8F16-2BC4A4768E65} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
5460
{12266784-3C6A-4598-B593-8F84C4BCC0D2} = {5A26CA6D-64DF-4A0F-9FE2-D6BE96AB3F38}
5561
{6CD9BEEC-158D-4CFE-B24F-A3E328782777} = {B2E4FF07-A0B1-40D5-A4E5-E1F778290C24}
62+
{7E369F8C-967F-4B9B-AF00-A9C486748272} = {B2E4FF07-A0B1-40D5-A4E5-E1F778290C24}
5663
EndGlobalSection
5764
GlobalSection(ExtensibilityGlobals) = postSolution
5865
SolutionGuid = {3D5741F5-9CEB-45E5-BD45-996AB1930EB7}

README.md

Lines changed: 176 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,176 @@
1-
# Nlog.RabbitMQ.Target
2-
RabbitMQ Target for popular NLog logging tool
3-
4-
[![NuGet](https://img.shields.io/nuget/v/Nlog.RabbitMQ.Target.svg)](https://www.nuget.org/packages/Nlog.RabbitMQ.Target/)
5-
[![master](https://github.com/artdolya/Nlog.RabbitMQ/actions/workflows/bump.yml/badge.svg)](https://github.com/artdolya/Nlog.RabbitMQ/actions/workflows/bump.yml)
6-
7-
## Minimum Recommended Configuration
8-
9-
```xml
10-
<?xml version="1.0" encoding="utf-8" ?>
11-
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
12-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
13-
14-
<extensions>
15-
<add assembly="Nlog.RabbitMQ.Target" />
16-
</extensions>
17-
18-
<targets async="true">
19-
<target name="RabbitMqTarget"
20-
xsi:type="RabbitMq"
21-
useJSON="true"
22-
layout="${message}" />
23-
</targets>
24-
25-
<rules>
26-
<logger name="*" minlevel="Trace" writeTo="RabbitMqTarget"/>
27-
</rules>
28-
29-
</nlog>
30-
```
31-
32-
Remember to mark your `NLog.config` file to be copied to the output directory!
33-
34-
## Full Configuration
35-
36-
```xml
37-
<?xml version="1.0" encoding="utf-8" ?>
38-
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
39-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
40-
41-
<extensions>
42-
<add assembly="Nlog.RabbitMQ.Target" />
43-
</extensions>
44-
45-
<targets>
46-
<!-- these are the defaults (except 'topic' and 'appid'): -->
47-
<target name="RabbitMq"
48-
xsi:type="RabbitMq"
49-
appid="NLog.RabbitMq.DemoApp"
50-
correlationId=""
51-
messageType=""
52-
topic="DemoApp.Logging.${level}"
53-
username="guest"
54-
password="guest"
55-
hostname="localhost"
56-
exchange="app-logging"
57-
exchangeType="topic"
58-
clientProvidedName=""
59-
port="5672"
60-
vhost="/"
61-
maxBuffer="10240"
62-
heartBeatSeconds="3"
63-
Timeout="3000"
64-
layout="${message}"
65-
messageSource="nlog://${hostname}/${logger}"
66-
contentType="text/plain"
67-
UseJSON="false"
68-
UseSsl="false"
69-
SslCertPath=""
70-
SslCertPassphrase=""
71-
Compression="None"
72-
DeliveryMode="NonPersistent">
73-
<field key="threadid" layout="${threadid}" />
74-
<field key="hostname" layout="${hostname}" />
75-
</target>
76-
</targets>
77-
78-
<rules>
79-
<logger name="*" minlevel="Trace" writeTo="RabbitMQTarget"/>
80-
</rules>
81-
82-
</nlog>
83-
```
84-
85-
**Recommendation - async wrapper target**
86-
87-
Make the targets tag look like this: `<targets async="true"> ... </targets>` so that
88-
a failure of communication with RabbitMQ doesn't slow the application down. With this configuration
89-
an overloaded message broker will have 10000 messages buffered in the logging application
90-
before messages start being discarded. A downed message broker will have its messages
91-
in the *inner* target (i.e. RabbitMQ-target), not in the async buffer (as the RabbitMQ-target
92-
will not block which is what AsyncWrapperTarget buffers upon).
93-
94-
## Important - shutting it down!
95-
96-
Because NLog doesn't expose a single method for shutting everything down (but loads automatically by static properties - the loggers' first invocation to the framework) - you need to add this code to the exit of your application!
97-
98-
```csharp
99-
LogManager.Shutdown();
100-
```
101-
102-
## Value-Add - How to use with LogStash?
103-
104-
Make sure you are using the flag `useJSON='true'` in your configuration, then you [download logstash](http://logstash.net/)! Place it in a folder, and add a file that you call 'logstash.conf' next to it:
105-
106-
```
107-
input {
108-
amqp {
109-
durable => true
110-
exchange => "app-logging"
111-
exclusive => false
112-
format => "json_event"
113-
host => "localhost"
114-
key => "#"
115-
name => ""
116-
passive => false
117-
password => "guest"
118-
port => 5672
119-
prefetch_count => 10
120-
ssl => false
121-
# tags => ... # array (optional)
122-
type => "nlog"
123-
user => "guest"
124-
verify_ssl => false
125-
vhost => "/"
126-
}
127-
}
128-
129-
output {
130-
# Emit events to stdout for easy debugging of what is going through
131-
# logstash.
132-
stdout { }
133-
134-
# This will use elasticsearch to store your logs.
135-
# The 'embedded' option will cause logstash to run the elasticsearch
136-
# server in the same process, so you don't have to worry about
137-
# how to download, configure, or run elasticsearch!
138-
elasticsearch { embedded => true }
139-
}
140-
```
141-
142-
You then start the monolithic logstash: `java -jar logstash-1.1.0-monolithic.jar agent -f logstash.conf -- web`.
143-
Now you can surf to http://127.0.0.1:9292 and search your logs that you're generating
1+
# Nlog.RabbitMQ.Target
2+
3+
RabbitMQ Target for popular NLog logging tool
4+
5+
[![NuGet](https://img.shields.io/nuget/v/Nlog.RabbitMQ.Target.svg)](https://www.nuget.org/packages/Nlog.RabbitMQ.Target/)
6+
[![master](https://github.com/artdolya/Nlog.RabbitMQ/actions/workflows/bump.yml/badge.svg)](https://github.com/artdolya/Nlog.RabbitMQ/actions/workflows/bump.yml)
7+
8+
## 🚀 Async RabbitMQ Target
9+
10+
**New in vNEXT:** The RabbitMQ target is now fully asynchronous! Logging to RabbitMQ no longer blocks your application threads, resulting in improved performance and reliability, especially under high load or when the broker is slow/unavailable.
11+
12+
### Benefits
13+
14+
- Non-blocking logging: your app remains responsive even if RabbitMQ is slow or down.
15+
- Improved throughput and scalability.
16+
- More robust error handling and buffering.
17+
18+
### Migration Notes
19+
20+
- The target itself is now async; you do **not** need to wrap it in `<targets async="true">` (though you still can for extra safety).
21+
- Buffering and message discarding now happen inside the RabbitMQ target. See `maxBuffer` for configuration.
22+
- If you previously relied on the sync behavior, review your error handling and shutdown logic.
23+
- No breaking changes to configuration, but you may want to tune `maxBuffer` and `Timeout` for your workload.
24+
25+
### Example Configuration
26+
27+
```xml
28+
<targets>
29+
<target name="RabbitMqTarget"
30+
xsi:type="RabbitMq"
31+
useJSON="true"
32+
layout="${message}" />
33+
</targets>
34+
```
35+
36+
## Minimum Recommended Configuration
37+
38+
```xml
39+
<?xml version="1.0" encoding="utf-8" ?>
40+
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
41+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
42+
43+
<extensions>
44+
<add assembly="Nlog.RabbitMQ.Target" />
45+
</extensions>
46+
47+
<targets async="true">
48+
<target name="RabbitMqTarget"
49+
xsi:type="RabbitMq"
50+
useJSON="true"
51+
layout="${message}" />
52+
</targets>
53+
54+
<rules>
55+
<logger name="*" minlevel="Trace" writeTo="RabbitMqTarget"/>
56+
</rules>
57+
58+
</nlog>
59+
```
60+
61+
Remember to mark your `NLog.config` file to be copied to the output directory!
62+
63+
## Full Configuration
64+
65+
```xml
66+
<?xml version="1.0" encoding="utf-8" ?>
67+
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
68+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
69+
70+
<extensions>
71+
<add assembly="Nlog.RabbitMQ.Target" />
72+
</extensions>
73+
74+
<targets>
75+
<!-- these are the defaults (except 'topic' and 'appid'): -->
76+
<target name="RabbitMq"
77+
xsi:type="RabbitMq"
78+
appid="NLog.RabbitMq.DemoApp"
79+
correlationId=""
80+
messageType=""
81+
topic="DemoApp.Logging.${level}"
82+
username="guest"
83+
password="guest"
84+
hostname="localhost"
85+
exchange="app-logging"
86+
exchangeType="topic"
87+
clientProvidedName=""
88+
port="5672"
89+
vhost="/"
90+
maxBuffer="10240"
91+
heartBeatSeconds="3"
92+
Timeout="3000"
93+
layout="${message}"
94+
messageSource="nlog://${hostname}/${logger}"
95+
contentType="text/plain"
96+
UseJSON="false"
97+
UseSsl="false"
98+
SslCertPath=""
99+
SslCertPassphrase=""
100+
Compression="None"
101+
DeliveryMode="NonPersistent">
102+
<field key="threadid" layout="${threadid}" />
103+
<field key="hostname" layout="${hostname}" />
104+
</target>
105+
</targets>
106+
107+
<rules>
108+
<logger name="*" minlevel="Trace" writeTo="RabbitMQTarget"/>
109+
</rules>
110+
111+
</nlog>
112+
```
113+
114+
**Recommendation - async wrapper target**
115+
116+
Make the targets tag look like this: `<targets async="true"> ... </targets>` so that
117+
a failure of communication with RabbitMQ doesn't slow the application down. With this configuration
118+
an overloaded message broker will have 10000 messages buffered in the logging application
119+
before messages start being discarded. A downed message broker will have its messages
120+
in the _inner_ target (i.e. RabbitMQ-target), not in the async buffer (as the RabbitMQ-target
121+
will not block which is what AsyncWrapperTarget buffers upon).
122+
123+
---
124+
125+
**Note:** As of vNEXT, the RabbitMQ target is itself asynchronous. Wrapping in `<targets async="true">` is optional and may provide additional buffering, but is no longer required for non-blocking logging.
126+
127+
## Important - shutting it down!
128+
129+
Because NLog doesn't expose a single method for shutting everything down (but loads automatically by static properties - the loggers' first invocation to the framework) - you need to add this code to the exit of your application!
130+
131+
```csharp
132+
LogManager.Shutdown();
133+
```
134+
135+
## Value-Add - How to use with LogStash?
136+
137+
Make sure you are using the flag `useJSON='true'` in your configuration, then you [download logstash](http://logstash.net/)! Place it in a folder, and add a file that you call 'logstash.conf' next to it:
138+
139+
```
140+
input {
141+
amqp {
142+
durable => true
143+
exchange => "app-logging"
144+
exclusive => false
145+
format => "json_event"
146+
host => "localhost"
147+
key => "#"
148+
name => ""
149+
passive => false
150+
password => "guest"
151+
port => 5672
152+
prefetch_count => 10
153+
ssl => false
154+
# tags => ... # array (optional)
155+
type => "nlog"
156+
user => "guest"
157+
verify_ssl => false
158+
vhost => "/"
159+
}
160+
}
161+
162+
output {
163+
# Emit events to stdout for easy debugging of what is going through
164+
# logstash.
165+
stdout { }
166+
167+
# This will use elasticsearch to store your logs.
168+
# The 'embedded' option will cause logstash to run the elasticsearch
169+
# server in the same process, so you don't have to worry about
170+
# how to download, configure, or run elasticsearch!
171+
elasticsearch { embedded => true }
172+
}
173+
```
174+
175+
You then start the monolithic logstash: `java -jar logstash-1.1.0-monolithic.jar agent -f logstash.conf -- web`.
176+
Now you can surf to http://127.0.0.1:9292 and search your logs that you're generating

0 commit comments

Comments
 (0)