patterncsharpMinor
Logging auditing information
Viewed 0 times
informationloggingauditing
Problem
I had to implement an auditing system where the user have to provide a reason he needs to perform a certain operation. When he provides the reason, he can perform freely it for a certain amount of time. Each execution of the operation needs to be logged somewhere.
I implemented it using a Continuation Passing Style, which allows me to cleanly handle atomically permission checking and operation logging.
What do you think about it?
Here it is some client code to show how it is supposed to be used.
```
public static void Main(string[] args)
{
var auditLogger = new AuditLogger(TimeSpan.FromMinutes(15), new AuditingDatabase);
var reason = args[0]; // Let's assume the user tells us what he needs to do at startup
auditLogger.SetAccessReason(reason);
// Now the user keeps doing lots of things
while(1)
{
auditLogger.LogAccessAndExecuteOperation(
() => { / doing nothing here / Thread.Sleep(1000); },
() => {
Console.WriteLine("Time's up. Can you please tell us again what you need to do?");
reason = Console.ReadLine();
auditLogger.SetAccessReason(reason);
},
exeption => Console.Wri
I implemented it using a Continuation Passing Style, which allows me to cleanly handle atomically permission checking and operation logging.
public class AuditLogger
{
private readonly TimeSpan permissionDuration;
private readonly IAuditDatabase auditDatabase;
private DateTime permissionExpiry = DateTime.MinValue;
private string accessReason;
public AuditLogger(TimeSpan permissionDuration, IAuditDatabase auditDatabase)
{
this.permissionDuration = permissionDuration;
this.auditDatabase = auditDatabase;
}
private bool CheckPermission()
{
return DateTime.UTCNow onError)
{
if(CheckPermission())
onPermissionExpired();
try
{
auditDatabase.Log(reason);
operation();
}
catch(Exception exception)
{
onError(exception);
}
}
}What do you think about it?
Here it is some client code to show how it is supposed to be used.
```
public static void Main(string[] args)
{
var auditLogger = new AuditLogger(TimeSpan.FromMinutes(15), new AuditingDatabase);
var reason = args[0]; // Let's assume the user tells us what he needs to do at startup
auditLogger.SetAccessReason(reason);
// Now the user keeps doing lots of things
while(1)
{
auditLogger.LogAccessAndExecuteOperation(
() => { / doing nothing here / Thread.Sleep(1000); },
() => {
Console.WriteLine("Time's up. Can you please tell us again what you need to do?");
reason = Console.ReadLine();
auditLogger.SetAccessReason(reason);
},
exeption => Console.Wri
Solution
The class unfortunately violates the Single Responsibility Principle because it has to deal with logging as well as expiring access.
You could use the Decorator Pattern here and split the authorization and logging into separate classes as follows:
You could use the Decorator Pattern here and split the authorization and logging into separate classes as follows:
public interface IAccessWidget
{
string AccessReason { get; }
void SetAccessReason(string reason);
void ExecuteOperation(Action operation, Action onPermissionExpired, Action onError);
}
public class AccessWidget
{
private readonly TimeSpan _permissionDuration;
private DateTime _permissionExpiry = DateTime.MinValue;
public string AccessReason { get; private set; }
public AccessWidget(TimeSpan permissionDuration)
{
_permissionDuration = permissionDuration;
}
private bool CheckPermission()
{
return DateTime.UtcNow onError)
{
if(CheckPermission())
onPermissionExpired();
try
{
operation();
}
catch(Exception exception)
{
onError(exception);
}
}
}
public class LoggingAccessWidget : IAccessWidget
{
private readonly IAuditDatabase _auditDatabase;
private readonly IAccessWidget _widget;
public string AccessReason { get { return _widget.AccessReason; } }
public LoggingAccessWidget(IAccessWidget widget, IAuditDatabase auditDatabase)
{
_widget = widget;
_auditDatabase = auditDatabase;
}
public void SetAccessReason(string reason)
{
_widget.SetAccessReason(reason);
}
public void ExecuteOperation(Action operation, Action onPermissionExpired, Action onError)
{
_auditDatabase.Log(_widget.AccessReason);
_widget.ExecuteOperation(operation, onPermissionExpired, onError);
}
}Code Snippets
public interface IAccessWidget
{
string AccessReason { get; }
void SetAccessReason(string reason);
void ExecuteOperation(Action operation, Action onPermissionExpired, Action<Exception> onError);
}
public class AccessWidget
{
private readonly TimeSpan _permissionDuration;
private DateTime _permissionExpiry = DateTime.MinValue;
public string AccessReason { get; private set; }
public AccessWidget(TimeSpan permissionDuration)
{
_permissionDuration = permissionDuration;
}
private bool CheckPermission()
{
return DateTime.UtcNow < _permissionExpiry;
}
public void SetAccessReason(string reason)
{
AccessReason = reason;
_permissionExpiry = DateTime.UtcNow.Add(_permissionDuration);
}
public void ExecuteOperation(Action operation, Action onPermissionExpired, Action<Exception> onError)
{
if(CheckPermission())
onPermissionExpired();
try
{
operation();
}
catch(Exception exception)
{
onError(exception);
}
}
}
public class LoggingAccessWidget : IAccessWidget
{
private readonly IAuditDatabase _auditDatabase;
private readonly IAccessWidget _widget;
public string AccessReason { get { return _widget.AccessReason; } }
public LoggingAccessWidget(IAccessWidget widget, IAuditDatabase auditDatabase)
{
_widget = widget;
_auditDatabase = auditDatabase;
}
public void SetAccessReason(string reason)
{
_widget.SetAccessReason(reason);
}
public void ExecuteOperation(Action operation, Action onPermissionExpired, Action<Exception> onError)
{
_auditDatabase.Log(_widget.AccessReason);
_widget.ExecuteOperation(operation, onPermissionExpired, onError);
}
}Context
StackExchange Code Review Q#64674, answer score: 4
Revisions (0)
No revisions yet.