Back to: Design Patterns in C# With Real-Time Examples
Real-Time Examples of Logging Proxy Design Pattern in C#
In this article, I will discuss Real-Time Examples of Logging Proxy Design Patterns in C#. Please read our previous article, discussing Real-Time Examples of the Cache Proxy Design Pattern in C#. At the end of this article, you will understand the following pointers.
- What is the Logging Proxy Design Pattern in C#?
- Multiple Real-Time Examples of Logging Proxy Design Pattern in C#
- Advantages and Disadvantages of Logging Proxy Design Pattern in C#
- When to use Logging Proxy Design Pattern in C#?
Logging Proxy Design Pattern in C#
The Proxy Design Pattern provides a surrogate or placeholder for another object to control access to it. One of its applications is in logging, where we want to record the operations performed on an object. In the Logging Proxy Design Pattern, the proxy acts as an intermediary between the client and the actual object and logs the operations the client requests before passing the request to the real object. Below is an example of the Logging Proxy Design Pattern in C#:
using System; namespace LoggingProxyDesignPattern { // Subject interface public interface ISubject { void PerformOperation(); } // RealSubject class public class RealSubject : ISubject { public void PerformOperation() { Console.WriteLine("Operation performed by RealSubject"); } } // Proxy class public class LoggingProxy : ISubject { private readonly ISubject _realSubject; private readonly ILogger _logger; public LoggingProxy(ISubject realSubject, ILogger logger) { _realSubject = realSubject ?? throw new ArgumentNullException(nameof(realSubject)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public void PerformOperation() { _logger.Log("PerformOperation method called on RealSubject"); _realSubject.PerformOperation(); } } // Logger interface public interface ILogger { void Log(string message); } // Console logger implementation public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine($"LOG: {message}"); } } //Client Code //Testing Logging Proxy Design Pattern public class Program { public static void Main() { ISubject subject = new RealSubject(); ILogger logger = new ConsoleLogger(); ISubject proxy = new LoggingProxy(subject, logger); // Client interacts with the proxy instead of the real subject proxy.PerformOperation(); Console.ReadKey(); } } }
In the example above, when the client calls PerformOperation on the proxy, the proxy logs the operation and then delegates the call to the RealSubject. You will get the following output when you run the above application.
Real-Time Example of Logging Proxy Design Pattern in C#
Letās consider a real-time example: an e-banking application. In such an application, whenever a user initiates a funds transfer, we want to log this operation for audit purposes. But, at the same time, we donāt want the core transfer logic combined with the logging logic.
using System; namespace LoggingProxyDesignPattern { //Subject Interface(Banking Operations): //This interface will define the operations that our bank supports. public interface IBankingOperation { void TransferFunds(string fromAccount, string toAccount, decimal amount); } //Real Subject(Actual Banking Operations): //This class will implement the IBankingOperation interface and perform the actual fund transfer. public class BankingService : IBankingOperation { public void TransferFunds(string fromAccount, string toAccount, decimal amount) { // Logic to transfer funds. Console.WriteLine($"Transferred {amount:C} from {fromAccount} to {toAccount}."); } } //Logging Proxy: //This is our proxy that will intercept calls to TransferFunds and log them. public class LoggingBankingProxy : IBankingOperation { private readonly IBankingOperation _bankingService; private readonly ILogger _logger; public LoggingBankingProxy(IBankingOperation bankingService, ILogger logger) { _bankingService = bankingService; _logger = logger; } public void TransferFunds(string fromAccount, string toAccount, decimal amount) { _logger.Log($"Initiating transfer of {amount:C} from {fromAccount} to {toAccount}."); _bankingService.TransferFunds(fromAccount, toAccount, amount); _logger.Log($"Transfer of {amount:C} from {fromAccount} to {toAccount} completed."); } } //Logger Interface & Console Logger Implementation: //This remains unchanged from the previous example. public interface ILogger { void Log(string message); } public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine($"LOG: {message}"); } } //Client Code //Testing Logging Proxy Design Pattern public class Program { public static void Main() { IBankingOperation bankingService = new BankingService(); ILogger logger = new ConsoleLogger(); IBankingOperation proxyBankingService = new LoggingBankingProxy(bankingService, logger); proxyBankingService.TransferFunds("AccountA", "AccountB", 1000m); Console.ReadKey(); } } }
In this real-time example, the proxy logs the operationās start and completion whenever a funds transfer is initiated using the proxy. The core banking service (BankingService) remains clean and focused only on the funds transfer logic.
Real-Time Example of Logging Proxy Design Pattern in C#: Online Shopping System
Letās use another real-time scenario to explain the Logging Proxy Design Pattern. In an online shopping system, thereās an operation to place an order. We want to log each order placement for tracking and auditing purposes.
using System; namespace LoggingProxyDesignPattern { //Subject Interface(Order Operations): //This interface will define the operation to place an order. public interface IOrderService { void PlaceOrder(string product, int quantity); } //Real Subject(Actual Order Service): //This class will implement the IOrderService interface and execute the actual order placement. public class OrderService : IOrderService { public void PlaceOrder(string product, int quantity) { // Logic to place an order. Console.WriteLine($"Order placed! Product: {product}, Quantity: {quantity}"); } } //Logging Proxy: //This is our proxy that will intercept calls to PlaceOrder and log them. public class LoggingOrderServiceProxy : IOrderService { private readonly IOrderService _orderService; private readonly ILogger _logger; public LoggingOrderServiceProxy(IOrderService orderService, ILogger logger) { _orderService = orderService; _logger = logger; } public void PlaceOrder(string product, int quantity) { _logger.Log($"Attempting to place order. Product: {product}, Quantity: {quantity}"); _orderService.PlaceOrder(product, quantity); _logger.Log($"Order successfully placed. Product: {product}, Quantity: {quantity}"); } } //Logger Interface & Console Logger Implementation: public interface ILogger { void Log(string message); } public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine($"[LOG]: {message}"); } } //Client Code //Testing Logging Proxy Design Pattern public class Program { public static void Main() { IOrderService orderService = new OrderService(); ILogger logger = new ConsoleLogger(); IOrderService proxyOrderService = new LoggingOrderServiceProxy(orderService, logger); proxyOrderService.PlaceOrder("Laptop", 1); Console.ReadKey(); } } }
In this real-time example from an online shopping system, when an order is placed using the proxy, it logs both the attempt to place the order and the successful placement. The core order service (OrderService) is solely responsible for placing the order and remains unaffected by logging concerns.
Advantages and Disadvantages of Logging Proxy Design Pattern in C#
The Logging Proxy Design Pattern, as applied to C# or any other programming language, has advantages and disadvantages. Letās discuss them:
Advantages of Logging Proxy Design Pattern in C#:
- Separation of Concerns: The logging functionality is separated from the main business logic. This ensures that the primary class remains focused on its core responsibility and is not cluttered with logging code.
- Flexibility: Logging mechanisms can be changed or enhanced without modifying the original class. If you switch from console logging to file logging or cloud-based logging, only the proxy needs to be changed.
- Reusability: The same logging proxy can be reused for multiple classes if they conform to the same interface.
- Non-intrusiveness: The original class remains unaware of the logging, making it pure in its operations. This can be particularly useful if the original class comes from a third-party library or is part of a system you donāt want to or canāt change.
- Consistent Logging: By routing all operations through the proxy, you ensure consistent logging format and behavior across different parts of your application.
Disadvantages of Logging Proxy Design Pattern in C#:
- Overhead: Introducing a proxy can add a slight runtime overhead, especially if the number of operations routed through the proxy is large.
- Complexity: For simple applications or operations, introducing a proxy just for logging can be overkill and adds unnecessary complexity.
- Maintenance: If the interface of the subject changes, the proxy needs to be updated accordingly. In larger systems, maintaining proxies can become tedious.
- Latency: Especially in systems where speed is critical, the added latency (although minimal) by the proxy could be a concern.
- Limited Scope: A logging proxy is specifically for logging. If you need additional functionalities like caching, validation, etc., you might end up with multiple proxies or a more complex proxy, further complicating the design.
While the Logging Proxy Design Pattern can be beneficial for maintaining clean code and separating concerns, evaluating whether its advantages outweigh its disadvantages in the context of a specific application is essential.
When to use Logging Proxy Design Pattern in C#?
The Logging Proxy Design Pattern is a specific application of the general Proxy Design Pattern. Itās focused on introducing logging behavior for operations performed on an object without altering or affecting it. Knowing when to use it is crucial for creating maintainable and efficient software. Here are some scenarios where the Logging Proxy Design Pattern might be useful:
- Audit and Compliance: In industries where operations need to be audited, like finance or health care, logging proxies can transparently record actions without changing the core logic.
- Troubleshooting and Debugging: When a system is intricate or has occasional hard-to-reproduce issues, logging proxies can provide detailed logs that might help diagnose the problems.
- Performance Monitoring: If you want to log the time taken for operations, a logging proxy can be an excellent place to implement this.
- Third-party Libraries: When you use external libraries or services and want to log operations without modifying the library code or when the library doesnāt provide built-in logging.
- Decoupling Logging Logic: If you want to keep your main business logic clean and free from logging code, a logging proxy can help separate these concerns.
- Flexible Logging Levels: If you want to implement different logging levels or dynamically change the amount and detail of logging based on various factors, a logging proxy can manage this without altering the main objectās behavior.
- Consistency Across Operations: If multiple operations or methods need consistent logging behavior, using a proxy can ensure uniform logging across them.
However, as with all design patterns, there are situations where the Logging Proxy might not be the best choice:
- Performance-Critical Applications: The overhead introduced by the proxy (though minimal) might not be acceptable in some high-performance scenarios.
- Simple Applications: Introducing a logging proxy might be overkill for very simple applications or for situations where logging is minimal and doesnāt risk cluttering the main logic.
- Multiple Behaviors: If an object needs multiple additional behaviors (like logging, caching, validation), you might need multiple proxies or a more complex proxy, which could complicate the design.
While the Logging Proxy Design Pattern is a powerful tool for cleanly introducing logging behavior, itās essential to analyze your applicationās needs and weigh the advantages against the potential complications.
In the next article, I will discuss the Real-Time Examples of Synchronization Proxy Design Patterns in C#. Here, in this article, I try to explain the Real-Time Examples of Logging Proxy Design Patterns in C#. I hope you enjoy this Logging Proxy Design Pattern in Real-time Examples using the C# article.
Registration Open For New Online Training
Enhance Your Professional Journey with Our Upcoming Live Session. For complete information on Registration, Course Details, Syllabus, and to get the Zoom Credentials to attend the free live Demo Sessions, please click on the below links.