Temporary logging suppression for a scope in Serilog - c#

I need to be able to temporarily disable logging for some scope. In my case there is a background task which periodically attempts to instantiate some device API for every available COM port in the system and sees if it fails. That API writes a lot of information to log in case of failure (exceptions, internal components Dispose calls, etc). As a result, the log is flooded with such unsuccessful attempts errors every second.
I came up with the solution that uses LogContext.PushProperty to identify the supressed log events. However, the following code won't log anything:
internal static class Program
{
public static void Main(String[] args)
{
void StartListeningSomething()
{
Task.Factory.StartNew(() =>
{
while (true)
{
Log.Information("Listening");
Thread.Sleep(500);
}
}, TaskCreationOptions.LongRunning);
}
Log.Logger = new LoggerConfiguration()
.Enrich.WithThreadId()
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("SuppressLogging"))
.Enrich.FromLogContext()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
using (LogContext.PushProperty("SuppressLogging", true))
{
StartListeningSomething();
Console.ReadKey(); // Will ignore background thread log messages until key enter
}
// We want to start logging events after exiting using block
// But they won't be logged for listener thread at all
Console.ReadKey();
}
}
All log events inside listener task will be enriched with "SupressLogging" property even after popping it from the scope.

The only workaround I found (except the tedious passing customized ILogger throughout the entire API) consists of the following steps:
Assign some unique value to "SupressLogging" property
Add this value to internal static storage
When exiting scope, remove this value from storage (invalidate)
In Filter section of the logger configuration, check for property being attached and it's value being valid (contained in storage).
The following code uses custom IDisposable token to make it seem like usual PushProperty
internal static class Program
{
public static void Main(String[] args)
{
void StartListeningSomething()
{
Task.Factory.StartNew(() =>
{
while (true)
{
Log.Information("Listening");
Thread.Sleep(500);
}
}, TaskCreationOptions.LongRunning);
}
Log.Logger = new LoggerConfiguration()
.Enrich.WithThreadId()
.Filter.ByExcluding(logEvent => logEvent.IsSuppressed()) // Check if log event marked with supression property
.Enrich.FromLogContext()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
using (SerilogExtensions.SuppressLogging())
{
StartListeningSomething();
Console.ReadKey(); // Will ignore background thread log messages until some key is entered
}
// Will start logging events after exiting the using block
Console.ReadKey();
}
}
And the actual SerilogExtensions:
/// <summary>
/// Provides helper extensions to Serilog logging.
/// </summary>
public static class SerilogExtensions
{
private const String SuppressLoggingProperty = "SuppressLogging";
private static readonly HashSet<Guid> ActiveSuppressions = new HashSet<Guid>();
/// <summary>
/// Get disposable token to supress logging for context.
/// </summary>
/// <remarks>
/// Pushes "SuppressLogging" property with unique value to SerilogContext.
/// When disposed, disposes Serilog property push token and invalidates stored value so new log messages are no longer
/// supressed.
/// </remarks>
public static IDisposable SuppressLogging()
{
return new SuppressLoggingDisposableToken();
}
/// <summary>
/// Determines whether the given log event suppressed.
/// </summary>
/// <remarks>
/// Also removes "SuppressLogging" property if present.
/// </remarks>
public static Boolean IsSuppressed(this LogEvent logEvent)
{
Boolean containsProperty = logEvent.Properties.TryGetValue(SuppressLoggingProperty, out var val);
if (!containsProperty)
return false;
logEvent.RemovePropertyIfPresent(SuppressLoggingProperty); //No need for that in logs
if (val is ScalarValue scalar && scalar.Value is Guid id)
return ActiveSuppressions.Contains(id);
return false;
}
/// <summary>
/// Disposable wrapper around logging supression property push/pop and value generation/invalidation.
/// </summary>
private class SuppressLoggingDisposableToken : IDisposable
{
private readonly IDisposable _pushPropertyDisposable;
private readonly Guid _guid;
public SuppressLoggingDisposableToken()
{
_guid = Guid.NewGuid();
_pushPropertyDisposable = LogContext.PushProperty(SuppressLoggingProperty, _guid);
ActiveSuppressions.Add(_guid);
}
public void Dispose()
{
ActiveSuppressions.Remove(_guid);
_pushPropertyDisposable.Dispose();
}
}
}
The complete example project can be found on github.
I would like to leave this self-answered question here and also to ask more experienced Serilog users what their opinion of this problem is. May be there is some common approach to logging suppression I didn't find?

I would like to add to ArXen42 answer.
The proposed Hashset to keep track of the the activesuppression keys is not threadsafe and will create issues when using multiple threads.
A solution would be to use the ConcurrentDictionary<T,T2> instead of the HashSet<T> or the solution as stated below without keeping track of GUIDs to supress the logs.
/// Provides helper extensions to Serilog logging.
/// </summary>
public static class SerilogExtensions
{
private const string SuppressLoggingProperty
= "SuppressLogging";
/// <summary>
/// Get disposable token to supress logging for context.
/// </summary>
public static IDisposable SuppressLogging()
{
return LogContext.PushProperty(SuppressLoggingProperty, true);
}
/// <summary>
/// Determines whether the given log event suppressed.
/// </summary>
/// <remarks>
/// Also removes "SuppressLogging" property if present.
/// </remarks>
public static bool IsSuppressed(this LogEvent logEvent)
{
var containsProperty = logEvent.Properties
.TryGetValue(SuppressLoggingProperty, out var val);
if (!containsProperty)
return false;
// remove suppression property from logs
logEvent.RemovePropertyIfPresent(SuppressLoggingProperty);
if (val is ScalarValue scalar && scalar.Value is bool isSuppressed)
return isSuppressed;
return false;
}
}

Related

Unit Testing using Moq and Autofac

I have the following logger logger class and I want to know the best to unit testing it.
Some observations:
I needed to create the interface IFileWrapper in order to break dependency with System.IO dependency and been able to user dependency injection (Autofac)
I was able to unit testing the method FileWrapper.WriteLog by implementing IFileWrapper using a MemoryString but if I wanted to test a expected behavior inside the method I won't be able (e.g: throwing exceptions, incorrect path and filename, etc.)
/// <summary>
/// Creates an instance of type <see cref="FileLogger"/>
/// </summary>
/// <remarks>Implements the Singleton Pattern</remarks>
private FileLogger()
{
FileName = string.Format("\\{0: MMM dd, yy}.log", DateTime.Now);
Path = Environment.CurrentDirectory;
FileWrapper = ContainerBuilderFactory.Container.Resolve<IFileWrapper>();
}
/// <summary>
/// Log the <paramref name="Message"/> in the <paramref name="Path"/> specified.
/// The <paramref name="UserName"/>, <paramref name="Host"/> must be supplied
/// </summary>
/// <example>
/// <code>
/// var handler = new LoggerHandlerFactory();
/// var logger = handler.GetHandler<FileLogger>();
/// logger.Log("Hello CSharpLogger");
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="NotSupportedException"></exception>
/// <exception cref="FileNotFoundException"></exception>
/// <exception cref="IOException"></exception>
/// <exception cref="SecurityException"></exception>
/// <exception cref="DirectoryNotFoundException"></exception>
/// <exception cref="UnauthorizedAccessException"></exception>
/// <exception cref="PathTooLongException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
/// <exception cref="FormatException"></exception>
public void Log(string message, LogLevel level = LogLevel.INFO)
{
lock (_current)
{
var configLevel = CSharpLoggerConfiguration.Configuration.GetLogLevel();
if (configLevel != LogLevel.OFF & level != LogLevel.OFF && configLevel >= level)
{
try
{
FileWrapper.WriteLog(string.Concat(Path, FileName), message, level);
}
catch (CSharpLoggerException)
{
throw;
}
}
}
}
So, I created the following UnitTesting using Moq:
//arrange
CSharpLoggerConfiguration.Configuration.SetLogLevel(LogLevel.DEBUG);
var mock = new Mock<IFileWrapper>();
mock.Setup(x => x.WriteLog(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<LogLevel>()));
logger.FileWrapper = mock.Object;
//act
logger.Log("Hello CSharpLogger", LogLevel.DEBUG);
logger.Log("Hello CSharpLogger", LogLevel.WARN);
//assert
mock.Verify(x => x.WriteLog(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<LogLevel>()), Times.Exactly(2));
So far so good. What I'm not confortable is with this line: logger.FileWrapper = mock.Object; I would like to keep FileWrapper propety private.
Any advise is welcome.
I'll be publishing the code http://csharplogger.codeplex.com/ in case you want more details.
Use constructor injection. In short; instead of providing the service (in this case the file wrapper) by setting a property, make the logger have a public constructor which takes an IFileWrapper argument.
public class Logger
{
public Logger(IFileWrapper fileWrapper)
{
FileWrapper = fileWrapper;
}
public IFileWrapper FileWrapper { get; }
}
// in your test:
var logger = new Logger(mock.Object);
To answer the question about having a singleton file wrapper more thoroughly, here's a code sample for the application (non-test) code:
public static class FileWrapperFactory
{
private static IFileWrapper _fileWrapper;
public static IFileWrapper GetInstance()
{
return _fileWrapper ?? (_fileWrapper = CreateInstance());
}
private static IFileWrapper CreateInstance()
{
// do all the necessary setup here
return new FileWrapper();
}
}
public class StuffDoer
{
public void DoStuff()
{
var logger = new FileLogger(FileWrapperFactory.GetInstance());
logger.WriteLog("Starting to do stuff...");
// do stuff
logger.WriteLog("Stuff was done.");
}
}
Since the FileWrapperFactory maintains a static instance of the file wrapper, you'll never have more than one. However, you can create multiple loggers like that, and they don't have to care. If you, in the future, decide that it's OK to have many file wrappers, the logger code doesn't have to change.
In a real-world application, I'd advice you to choose some kind of DI framework to handle all this book-keeping for you; most have excellent support for singleton instances, doing essentially what the FileWrapperFactory above does (but usually in a more sophisticated and robust way. FileWrapperFactory isnt' thread-safe, for example...).
Since your code comments show that your logger is a singleton, you need a way other than constructor injection for setting the dependency. In his book on Legacy Code, Mike Feathers suggests a function for such purposes, which is adequately named, something like
public void SetInstanceForTesting(IFileWrapper fileWrapper) {...}
Now this function won't hopefully be used for different purposes...

Trial experience of app is allowing unrestricted access - Windows Phone 8

I launched my first app yesterday to the store. The app has a trial version which simply restricts full access to the game.
I downloaded the trial and played for a while, no problems - good.
I then purchased the full version by from the store and started the app again only to find that it didnt release it's restrited areas - NOT good.
I have implemented the following code in the app to implement the restrictions:
/// <summary>
/// The TrialExperienceHelper class can serve as a convenient building-block in your app's trial experience implementation. It queries
/// the license as infrequently as possible, and caches the results, for maximum performance. When built in debug configuration, the class simulates the purchase
/// experience when the Buy method is called. To customize what happens in debug when Buy is called, initialize the simulatedLicMode and/or
/// simulatedLicModeOnPurchase fields to different values. A release build of this class will have access to a license only when the app is
/// published to the Windows Phone Store. For a release build not published to the Store, the value of the LicenseMode property will be
/// LicenseModes.MissingOrRevoked.
/// </summary>
public static class TrialExperienceHelper
{
#region enums
/// <summary>
/// The LicenseModes enumeration describes the mode of a license.
/// </summary>
public enum LicenseModes
{
Full,
MissingOrRevoked,
Trial
}
#endregion enums
#region fields
#if DEBUG
// Determines how a debug build behaves on launch. This field is set to LicenseModes.Full after simulating a purchase.
// Calling the Buy method (or navigating away from the app and back) will simulate a purchase.
internal static LicenseModes simulatedLicMode = LicenseModes.Trial;
#endif // DEBUG
private static bool isActiveCache;
private static bool isTrialCache;
#endregion fields
#region constructors
// The static constructor effectively initializes the cache of the state of the license when the app is launched. It also attaches
// a handler so that we can refresh the cache whenever the license has (potentially) changed.
static TrialExperienceHelper()
{
TrialExperienceHelper.RefreshCache();
PhoneApplicationService.Current.Activated += (object sender, ActivatedEventArgs e) => TrialExperienceHelper.
#if DEBUG
// In debug configuration, when the user returns to the application we will simulate a purchase.
OnSimulatedPurchase();
#else // DEBUG
// In release configuration, when the user returns to the application we will refresh the cache.
RefreshCache();
#endif // DEBUG
}
#endregion constructors
#region properties
/// <summary>
/// The LicenseMode property combines the active and trial states of the license into a single
/// enumerated value. In debug configuration, the simulated value is returned. In release configuration,
/// if the license is active then it is either trial or full. If the license is not active then
/// it is either missing or revoked.
/// </summary>
public static LicenseModes LicenseMode
{
get
{
#if DEBUG
return simulatedLicMode;
#else // DEBUG
if (TrialExperienceHelper.isActiveCache)
{
return TrialExperienceHelper.isTrialCache ? LicenseModes.Trial : LicenseModes.Full;
}
else // License is inactive.
{
return LicenseModes.MissingOrRevoked;
}
#endif // DEBUG
}
}
/// <summary>
/// The IsFull property provides a convenient way of checking whether the license is full or not.
/// </summary>
public static bool IsFull
{
get
{
return (TrialExperienceHelper.LicenseMode == LicenseModes.Full);
}
}
#endregion properties
#region methods
/// <summary>
/// The Buy method can be called when the license state is trial. the user is given the opportunity
/// to buy the app after which, in all configurations, the Activated event is raised, which we handle.
/// </summary>
public static void Buy()
{
MarketplaceDetailTask marketplaceDetailTask = new MarketplaceDetailTask();
marketplaceDetailTask.ContentType = MarketplaceContentType.Applications;
marketplaceDetailTask.Show();
}
/// <summary>
/// This method can be called at any time to refresh the values stored in the cache. We re-query the application object
/// for the current state of the license and cache the fresh values. We also raise the LicenseChanged event.
/// </summary>
public static void RefreshCache()
{
TrialExperienceHelper.isActiveCache = CurrentApp.LicenseInformation.IsActive;
TrialExperienceHelper.isTrialCache = CurrentApp.LicenseInformation.IsTrial;
TrialExperienceHelper.RaiseLicenseChanged();
}
private static void RaiseLicenseChanged()
{
if (TrialExperienceHelper.LicenseChanged != null)
{
TrialExperienceHelper.LicenseChanged();
}
}
#if DEBUG
private static void OnSimulatedPurchase()
{
TrialExperienceHelper.simulatedLicMode = LicenseModes.Full;
TrialExperienceHelper.RaiseLicenseChanged();
}
#endif // DEBUG
#endregion methods
#region events
/// <summary>
/// The static LicenseChanged event is raised whenever the value of the LicenseMode property has (potentially) changed.
/// </summary>
public static event LicenseChangedEventHandler LicenseChanged;
#endregion events
}
Then in my code I'm using if's to determine what to allow with this:
if ((TrialExperienceHelper.LicenseMode == TrialExperienceHelper.LicenseModes.Trial)||
(TrialExperienceHelper.LicenseMode == TrialExperienceHelper.LicenseModes.MissingOrRevoked))
{
//Then add ad' etc
}
This call must be returning TRUE...!
For the life of me I can't see the mistake, maybe someone here more experienced can help? What have I done wrong here?

Return a string from a Service already running using Remoting

im trying to use .Net Remoting to get a value of a variable that i use in a thread of an windows service.
TcpChannel tcpChannel = new TcpChannel(9998);
ChannelServices.RegisterChannel(tcpChannel, false);
Type commonInterfaceType = typeof(MyNameSpace.Core.Engine);
RemotingConfiguration.RegisterWellKnownServiceType(commonInterfaceType,
"CopyFilePercentage",
WellKnownObjectMode.SingleCall);
myEngine = Engine.EngineInstance;
myEngine.Start();
But it seams that every time that i use the Client to get that value, a new thread is created returning an empty string.
Any idea why is this happening or am I doing something wrong?
Thanks in advance,
Miguel de Sousa
WellKnownObjectMode.SingleCall creates a new instance of your class for each call. try WellKnownObjectMode.Singleton
EDIT
Maybe you should read about client activated objects. Turn your singleton object to a class factory and return a new instance of a real worker class(ofcourse inheriting from MarshalByRefObject) which will be used by the client.
so your client will be something like this
var worker = client.GetWorkerClass();
worker.GetSomeData();
and you will have one server object per connection (this may not be the correct terminology).
well i just used a Global Variable Class not really what I wanted but does the job.
/// <summary>
/// Contains global variables for project.
/// </summary>
public static class GlobalVar
{
/// <summary>
/// Global variable that is constant.
/// </summary>
public const string GlobalString = "Important Text";
/// <summary>
/// Static value protected by access routine.
/// </summary>
static int _globalValue;
/// <summary>
/// Access routine for global variable.
/// </summary>
public static int GlobalValue
{
get
{
return _globalValue;
}
set
{
_globalValue = value;
}
}
/// <summary>
/// Global static field.
/// </summary>
public static bool GlobalBoolean;
}

What am I doing wrong with my Workflow bookmark pick trigger?

I have a NativeActivity derived activity that I wrote that is to use bookmarks as a trigger for a pick branch. Using something I found on MSDN I tried writing this to trigger the branch. The branch contains activities that fire service callbacks to remote clients via send activities. If I set a delay for the trigger, callbacks fire to the clients successfully. If I use my code activity, the pick branch activities don't fire.
public sealed class UpdateListener : NativeActivity<ClientUpdate>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override void Execute(NativeActivityContext context)
{
context.CreateBookmark(BookmarkName.Get(context),
new BookmarkCallback(this.OnResumeBookmark));
}
protected override bool CanInduceIdle
{
get { return true; }
}
public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj )
{
Result.Set(context, (ClientUpdate)obj);
}
}
So it takes an arg to set the bookmark name for future bookmark references to execute the trigger. OnResumeBoookmark() takes in a ClientUpdate object that is passed by my application that is hosting the workflowapp. The activity is to return the object so the ClientUpdate can be passed to the workflow and have it sent to the remote clients via the send activity in the pick branch. In theory anyways.
For some reason it seems to be correct but feels wrong. I'm not sure if I should write the Activity in a different way to take care of what I need for my WF service.
I think your intentions would be a bit clearer if you created an extension (that implements IWorkflowInstanceExtension) to perform your action here.
For example:
public sealed class AsyncWorkExtension
: IWorkflowInstanceExtension
{
// only one extension per workflow
private WorkflowInstanceProxy _proxy;
private Bookmark _lastBookmark;
/// <summary>
/// Request the extension does some work for an activity
/// during which the activity will idle the workflow
/// </summary>
/// <param name="toResumeMe"></param>
public void DoWork(Bookmark toResumeMe)
{
_lastBookmark = toResumeMe;
// imagine I kick off some async op here
// when complete system calls WorkCompleted below
// NOTE: you CANNOT block here or you block the WF!
}
/// <summary>
/// Called by the system when long-running work is complete
/// </summary>
/// <param name="result"></param>
internal void WorkCompleted(object result)
{
//NOT good practice! example only
//this leaks resources search APM for details
_proxy.BeginResumeBookmark(_lastBookmark, result, null, null);
}
/// <summary>
/// When implemented, returns any additional extensions
/// the implementing class requires.
/// </summary>
/// <returns>
/// A collection of additional workflow extensions.
/// </returns>
IEnumerable<object> IWorkflowInstanceExtension
.GetAdditionalExtensions()
{
return new object[0];
}
/// <summary>
/// Sets the specified target
/// <see cref="WorkflowInstanceProxy"/>.
/// </summary>
/// <param name="instance">The target workflow instance to set.</param>
void IWorkflowInstanceExtension
.SetInstance(WorkflowInstanceProxy instance)
{
_proxy = instance;
}
}
Within the Activity, you'd use this thusly:
var ext = context.GetExtension<AsyncWorkExtension>();
var bookmark = context.CreateBookmark(BookmarkCallback);
ext.DoWork(bookmark);
return;
This way is much more explicit (instead of using the bookmark name to convey meaning to the "outside" world) and is much easier to extend if, say, you require to send out more information than a bookmark name.
Is there something actually resuming the bookmark here? If not the workflow will wait very patiently and nothing will happen.

Should I test UDP server code, and if so - why and how?

I don't have much experience doing unit testing. From what I learned, code should be decoupled, and I should not strive to test private code, just public methods, setters, etc etc.
Now, I have grasped some basic testing concepts, but I have troubles applying more advanced stuff to this case... Dependency Injection, Inversion of Control, Mock Objects, etc - can't get my head around it just yet :(
Before I move on to code, here are the questions.
In a given class, what exactly should I try to test?
How can I accomplish those test tasks?
Is there something seriously wrong with class design, which prevents testing from being done properly (or is something just plain wrong even outside of testing context)?
What design patterns are useful to testing network code in general?
Also, I've been trying to obey "write tests first, then write code to make tests pass", that's why I wrote first two tests that simply instantiate class and run it, but then, when server was able to start and accept packets, I didn't know what to test next...
Okay, here goes the code snippet. (note: original code is split in several namespaces, that's why it may appear a little out of order)
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace MyProject1
{
/// <summary>
/// Packet buffer that is sent to/received from connection
/// </summary>
public class UDPPacketBuffer
{
/// <summary>
/// Buffer size constant
/// </summary>
public const int BUFFER_SIZE = 4096;
private byte[] _data;
/// <summary>
/// Byte array with buffered data
///
/// DataLength is automatically updated when Data is set
/// </summary>
/// <see cref="DataLength"/>
public byte[] Data { get { return _data; } set { _data = value; DataLength = value.Length; } }
/// <summary>
/// Integer with length of buffered data
/// </summary>
public int DataLength;
/// <summary>
/// Remote end point (IP Address and Port)
/// </summary>
public EndPoint RemoteEndPoint;
/// <summary>
/// Initializes <see cref="UDPPacketBuffer"/> class
/// </summary>
public UDPPacketBuffer()
{
// initialize byte array
this.Data = new byte[BUFFER_SIZE];
// this will be filled in by the caller (eg. udpSocket.BeginReceiveFrom)
RemoteEndPoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
}
/// <summary>
/// Returns <see cref="Data"/> as a byte array shortened to <see cref="DataLength"/> number of bytes
/// </summary>
public byte[] ByteContent
{
get
{
if (DataLength > 0)
{
byte[] content = new byte[DataLength];
for (int i = 0; i < DataLength; i++)
content[i] = Data[i];
return content;
}
else
{
return Data;
}
}
}
/// <summary>
/// Returns <see cref="ByteContent"/> converted to string
/// </summary>
public string StringContent { get { return Encoding.ASCII.GetString(ByteContent); } }
}
/// <summary>
/// UDP packet-related event arguments passed when invoking events
/// </summary>
/// <example>
/// This example shows how to use UDPPacketEventArgs class when event is invoked.
/// <code>
/// if (PacketSent != null)
/// PacketSent(this, new UDPPacketEventArgs(buffer, bytesSent));
/// </code>
/// </example>
public class UDPPacketEventArgs : EventArgs
{
/// <summary>
/// Instance of UDPPacketBuffer, holding current event-related buffer
/// </summary>
public UDPPacketBuffer buffer { get; private set; }
/// <summary>
/// Number of bytes sent to remote end point
/// </summary>
public int sent { get; private set; }
/// <summary>
/// Initializes <see cref="buffer"/> only. Used when receiving data.
/// </summary>
/// <param name="buffer">Buffer sent to or received from remote endpoint</param>
public UDPPacketEventArgs(UDPPacketBuffer buffer)
{
this.buffer = buffer;
}
/// <summary>
/// Initializes <see cref="buffer"/> and <see cref="sent"/> variables. Used when sending data.
/// </summary>
/// <param name="buffer">buffer that has been sent</param>
/// <param name="sent">number of bytes sent</param>
public UDPPacketEventArgs(UDPPacketBuffer buffer, int sent)
{
this.buffer = buffer;
this.sent = sent;
}
}
/// <summary>
/// Asynchronous UDP server
/// </summary>
public class AsyncUdp : ServerBase
{
private const int _defaultPort = 45112;
private int _udpPort;
/// <summary>
/// Port number on which server should listen
/// </summary>
public int udpPort { get { return _udpPort; } private set { _udpPort = value; } }
// should server listen for broadcasts?
private bool broadcast = false;
// server socket
private Socket udpSocket;
// the ReaderWriterLock is used solely for the purposes of shutdown (Stop()).
// since there are potentially many "reader" threads in the internal .NET IOCP
// thread pool, this is a cheaper synchronization primitive than using
// a Mutex object. This allows many UDP socket "reads" concurrently - when
// Stop() is called, it attempts to obtain a writer lock which will then
// wait until all outstanding operations are completed before shutting down.
// this avoids the problem of closing the socket with outstanding operations
// and trying to catch the inevitable ObjectDisposedException.
private ReaderWriterLock rwLock = new ReaderWriterLock();
// number of outstanding operations. This is a reference count
// which we use to ensure that the threads exit cleanly. Note that
// we need this because the threads will potentially still need to process
// data even after the socket is closed.
private int rwOperationCount = 0;
// the all important shutdownFlag. This is synchronized through the ReaderWriterLock.
private bool shutdownFlag = true;
/// <summary>
/// Returns server running state
/// </summary>
public bool IsRunning
{
get { return !shutdownFlag; }
}
/// <summary>
/// Initializes UDP server with arbitrary default port
/// </summary>
public AsyncUdp()
{
this.udpPort = _defaultPort;
}
/// <summary>
/// Initializes UDP server with specified port number
/// </summary>
/// <param name="port">Port number for server to listen on</param>
public AsyncUdp(int port)
{
this.udpPort = port;
}
/// <summary>
/// Initializes UDP server with specified port number and broadcast capability
/// </summary>
/// <param name="port">Port number for server to listen on</param>
/// <param name="broadcast">Server will have broadcasting enabled if set to true</param>
public AsyncUdp(int port, bool broadcast)
{
this.udpPort = port;
this.broadcast = broadcast;
}
/// <summary>
/// Raised when packet is received via UDP socket
/// </summary>
public event EventHandler PacketReceived;
/// <summary>
/// Raised when packet is sent via UDP socket
/// </summary>
public event EventHandler PacketSent;
/// <summary>
/// Starts UDP server
/// </summary>
public override void Start()
{
if (! IsRunning)
{
// create and bind the socket
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, udpPort);
udpSocket = new Socket(
AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
udpSocket.EnableBroadcast = broadcast;
// we don't want to receive our own broadcasts, if broadcasting is enabled
if (broadcast)
udpSocket.MulticastLoopback = false;
udpSocket.Bind(ipep);
// we're not shutting down, we're starting up
shutdownFlag = false;
// kick off an async receive. The Start() method will return, the
// actual receives will occur asynchronously and will be caught in
// AsyncEndRecieve().
// I experimented with posting multiple AsyncBeginReceives() here in an attempt
// to "queue up" reads, however I found that it negatively impacted performance.
AsyncBeginReceive();
}
}
/// <summary>
/// Stops UDP server, if it is running
/// </summary>
public override void Stop()
{
if (IsRunning)
{
// wait indefinitely for a writer lock. Once this is called, the .NET runtime
// will deny any more reader locks, in effect blocking all other send/receive
// threads. Once we have the lock, we set shutdownFlag to inform the other
// threads that the socket is closed.
rwLock.AcquireWriterLock(-1);
shutdownFlag = true;
udpSocket.Close();
rwLock.ReleaseWriterLock();
// wait for any pending operations to complete on other
// threads before exiting.
while (rwOperationCount > 0)
Thread.Sleep(1);
}
}
/// <summary>
/// Dispose handler for UDP server. Stops the server first if it is still running
/// </summary>
public override void Dispose()
{
if (IsRunning == true)
this.Stop();
}
private void AsyncBeginReceive()
{
// this method actually kicks off the async read on the socket.
// we aquire a reader lock here to ensure that no other thread
// is trying to set shutdownFlag and close the socket.
rwLock.AcquireReaderLock(-1);
if (!shutdownFlag)
{
// increment the count of pending operations
Interlocked.Increment(ref rwOperationCount);
// allocate a packet buffer
UDPPacketBuffer buf = new UDPPacketBuffer();
try
{
// kick off an async read
udpSocket.BeginReceiveFrom(
buf.Data,
0,
UDPPacketBuffer.BUFFER_SIZE,
SocketFlags.None,
ref buf.RemoteEndPoint,
new AsyncCallback(AsyncEndReceive),
buf);
}
catch (SocketException)
{
// an error occurred, therefore the operation is void. Decrement the reference count.
Interlocked.Decrement(ref rwOperationCount);
}
}
// we're done with the socket for now, release the reader lock.
rwLock.ReleaseReaderLock();
}
private void AsyncEndReceive(IAsyncResult iar)
{
// Asynchronous receive operations will complete here through the call
// to AsyncBeginReceive
// aquire a reader lock
rwLock.AcquireReaderLock(-1);
if (!shutdownFlag)
{
// start another receive - this keeps the server going!
AsyncBeginReceive();
// get the buffer that was created in AsyncBeginReceive
// this is the received data
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
try
{
// get the length of data actually read from the socket, store it with the
// buffer
buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
// this operation is now complete, decrement the reference count
Interlocked.Decrement(ref rwOperationCount);
// we're done with the socket, release the reader lock
rwLock.ReleaseReaderLock();
// run event PacketReceived(), passing the buffer that
// has just been filled from the socket read.
if (PacketReceived != null)
PacketReceived(this, new UDPPacketEventArgs(buffer));
}
catch (SocketException)
{
// an error occurred, therefore the operation is void. Decrement the reference count.
Interlocked.Decrement(ref rwOperationCount);
// we're done with the socket for now, release the reader lock.
rwLock.ReleaseReaderLock();
}
}
else
{
// nothing bad happened, but we are done with the operation
// decrement the reference count and release the reader lock
Interlocked.Decrement(ref rwOperationCount);
rwLock.ReleaseReaderLock();
}
}
/// <summary>
/// Send packet to remote end point speified in <see cref="UDPPacketBuffer"/>
/// </summary>
/// <param name="buf">Packet to send</param>
public void AsyncBeginSend(UDPPacketBuffer buf)
{
// by now you should you get the idea - no further explanation necessary
rwLock.AcquireReaderLock(-1);
if (!shutdownFlag)
{
try
{
Interlocked.Increment(ref rwOperationCount);
udpSocket.BeginSendTo(
buf.Data,
0,
buf.DataLength,
SocketFlags.None,
buf.RemoteEndPoint,
new AsyncCallback(AsyncEndSend),
buf);
}
catch (SocketException)
{
throw new NotImplementedException();
}
}
rwLock.ReleaseReaderLock();
}
private void AsyncEndSend(IAsyncResult iar)
{
// by now you should you get the idea - no further explanation necessary
rwLock.AcquireReaderLock(-1);
if (!shutdownFlag)
{
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
try
{
int bytesSent = udpSocket.EndSendTo(iar);
// note that in invocation of PacketSent event - we are passing the number
// of bytes sent in a separate parameter, since we can't use buffer.DataLength which
// is the number of bytes to send (or bytes received depending upon whether this
// buffer was part of a send or a receive).
if (PacketSent != null)
PacketSent(this, new UDPPacketEventArgs(buffer, bytesSent));
}
catch (SocketException)
{
throw new NotImplementedException();
}
}
Interlocked.Decrement(ref rwOperationCount);
rwLock.ReleaseReaderLock();
}
}
/// <summary>
/// Base class used for all network-oriented servers.
/// <para>Disposable. All methods are abstract, including Dispose().</para>
/// </summary>
/// <example>
/// This example shows how to inherit from ServerBase class.
/// <code>
/// public class SyncTcp : ServerBase {...}
/// </code>
/// </example>
abstract public class ServerBase : IDisposable
{
/// <summary>
/// Starts the server.
/// </summary>
abstract public void Start();
/// <summary>
/// Stops the server.
/// </summary>
abstract public void Stop();
#region IDisposable Members
/// <summary>
/// Cleans up after server.
/// <para>It usually calls Stop() if server is running.</para>
/// </summary>
public abstract void Dispose();
#endregion
}
}
"Test code" follows.
namespace MyProject1
{
class AsyncUdpTest
{
[Fact]
public void UdpServerInstance()
{
AsyncUdp udp = new AsyncUdp();
Assert.True(udp is AsyncUdp);
udp.Dispose();
}
[Fact]
public void StartStopUdpServer()
{
using (AsyncUdp udp = new AsyncUdp(5000))
{
udp.Start();
Thread.Sleep(5000);
}
}
string udpReceiveMessageSend = "This is a test message";
byte[] udpReceiveData = new byte[4096];
bool udpReceivePacketMatches = false;
[Fact]
public void UdpServerReceive()
{
using (AsyncUdp udp = new AsyncUdp(5000))
{
udp.Start();
udp.PacketReceived += new EventHandler(delegate(object sender, EventArgs e)
{
UDPPacketEventArgs ea = e as UDPPacketEventArgs;
if (this.udpReceiveMessageSend.Equals(ea.buffer.StringContent))
{
udpReceivePacketMatches = true;
}
});
// wait 20 ms for a socket to be bound etc
Thread.Sleep(20);
UdpClient sock = new UdpClient();
IPEndPoint iep = new IPEndPoint(IPAddress.Loopback, 5000);
this.udpReceiveData = Encoding.ASCII.GetBytes(this.udpReceiveMessageSend);
sock.Send(this.udpReceiveData, this.udpReceiveData.Length, iep);
sock.Close();
// wait 20 ms for an event to fire, it should be enough
Thread.Sleep(20);
Assert.True(udpReceivePacketMatches);
}
}
}
}
note: code is c#, testing framework xUnit
A big thanks to everyone who takes time to go through my question and answer it!
Should you test ? Absolutely. You need to engineer your code for testability to make this simple. Your first statement is largely correct. So, some further comments:
Unit testing is largely testing code alone against test data and not reliant on external systems/servers etc. Functional/integration testing then brings in your external servers/databases etc. You can use dependency injection to inject either that real external system reference, or a test (mocked) system implementing the same interface, and thus your code becomes easily testable.
So in the above you would probably want to inject the UDP data source into your receiver. Your data source would implement a particular interface, and a mocked (or simple test) source would provide different packets for testing (e.g. empty, containing valid data, containing invalid data). That would form the basis of your unit test.
Your integration (or functional? I never know what to call it) test would perhaps start up a test UDP data source in the same VM, for each test, and pump data via UDP to your receiver. So now you've tested the basic functionality in the face of different packets via your unit test, and you're testing the actual UDP client/server function via your integration test.
So now you've tested your packet transmission/reception, you can test further parts of your code. Your UDP receiver will plug into another component, and here you can use dependency injection to inject the UDP receiver into your upstream component, or a mocked/test receiver implementing the same interface (and so on).
(Note: given that UDP transmission is unreliable even intra-host you should be prepared to cater for that somehow, or accept that infrequently you'll have problems. But that's a UDP-specific issue).
I notices some design problems in your code, and I think that this problems also interfere with ability to test this code.
I don't really unserstand UDPPacketBuffer class purpose. This class isn't encapsulate anything. It contains read/write Data property and I noticed only one probably useful is StringContent.
If you assume pass through UDP some application level packets, maybe you should create appropriate abstractions for this packets. Also, using UDP you should create something that helps you gather all parts in one (because you can receive parts of your packets in different order).
Also, I don't understand why your UDPPacketBuffer contains IPEndPoint.
That's why you can't test this class, because there no obvious purpose for this class.
It's really hard to test class that sends and receives data over network. But I notices some problems with your AsyncUdp implementation.
2.1 There is no packets delivery guarantee. I mean, who responsible for reliable packet delivery?
2.2 Ther is no thread safe (due to lack of exception safety).
What happens if Start method would be called simultaneously from separate threads?
And, consider following code (From Stop method):
rwLock.AcquireWriterLock(-1);
shutdownFlag = true;
udpSocket.Close();
rwLock.ReleaseWriterLock();
What if updSocket.Close method raise an exception? In this case rwLock whould stay in acquired state.
And in AsyncBeginReceive: what if UDPPacketBuffer ctor throws an exception, or udpSocket.BeginReceiveFrom throws SecurityException or ArgumentOutOfRangeException.
Other functions also not thread safe due to unhandled exception.
In this case you may create some helper class, that can be used in using statemant.
Something like this:
class ReadLockHelper : IDisposable
{
public ReadLockHelper(ReaderWriterLockSlim rwLock)
{
rwLock.AcquireReadLock(-1);
this.rwLock = rwLock;
}
public void Dispose()
{
rwLock.ReleaseReadLock();
}
private ReaderWriterLockSlim rwLock;
}
And than use it in your methods:
using (var l = new ReadLockHelper(rwLock))
{
//all other stuff
}
And finally. You should use ReaderWriterLockSlim instead ReaderWriterLock.
Important note from MSDN:
The .NET Framework has two reader-writer locks, ReaderWriterLockSlim and ReaderWriterLock. ReaderWriterLockSlim is recommended for all new development. ReaderWriterLockSlim is similar to ReaderWriterLock, but it has simplified rules for recursion and for upgrading and downgrading lock state. ReaderWriterLockSlim avoids many cases of potential deadlock. In addition, the performance of ReaderWriterLockSlim is significantly better than ReaderWriterLock.

Categories