5

I have one large monolithic application with four layers for specific functional requirements.

UI Layer -> Presentation Logic Layer -> Business Logic Layer -> Persistent Layer

One minimal working example for call flow can be like,

class ProductViewController {
    func showProduct(list){
        // populate list in view
    }
}

class ProductPresenter {
    func sanitiseProduct(list){
        // apply presentation logic to list
        viewController.showProduct(list)
    }
}

class ProductService {
    func filerProducts(list){
        // apply filtering logic to list
        productPresenter.sanitiseProduct(list)
    }
}


class ProductDatabase {
    func retrieveProducts(){
        // retrieve raw product list
        productService.filerProducts(getAllProduct())
    }
}

Now if any exception happens in any layer of the flow (i.e. query exception in Database layer) I have decided to log it in each layer with appropriate TAG and info and throw back to upper layers for propagating so that while debugging, each layer can filter its own logs using appropriate TAG without looking into other layers (i.e. especially when different teams are responsible for different layers).

While reviewing, one of my colleagues commented that in my design, there will be duplication of logs for a single exception/error which might cost performance and memory. His suggestion is to apply logging in one of the layers for specific exceptions (i.e. query exception in Persistent Layer only). However, he suggested to continue throwing the exception to upper layers.

Is my logging approach which provides better maintainability should be changed for the sake of performance and memory? What are the general suggestions to deal with this situation?

Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256
  • 1
    Log at the DEBUG level everywhere. Log at the ERROR level only if the code takes a different logic path as a result, i.e. doesn't just rethrow. But hopefully you're not getting so many of these that logging them is a performance concern. – John Wu Jan 12 '20 at 07:43
  • The more I think about this question, the more I realize it is subjective and not objective. I would down-vote it, but you gave it a bounty so you really want an answer. While I commend you for thinking seriously about your problem, perhaps StackOverflow is the wrong place to ask. I would recommend another site, but I don't know one so at present what you are doing is the best course of action. :) – Guy Coder Jan 15 '20 at 13:33

6 Answers6

5

The answer is most likly the dreaded.. It depends. I mean if you have issues with performance or memory sure every little bit helps. Also having duplicated log entries can give other issues (like every team looking at the log entry/error even if it is not relevant to them. Spending time looking at irrelevant log entries might be a waste of good time for the teams even if they can see the tagging pretty fast). Logging it only at the source might be a good thing if that is an issue.

That said maintanability and such are also positives that should be considered specially with large monolith applications that will live for a long time most likely. I have fallen many times in the trap of making things too complex in hopes of building the perfect solution but the added complexity makes it so hard to maintain that it has the opposite effect of being really bad.

So my suggestion is if there are no current issue with memory, performance or such, maintainability wins for longlived monolithic applications. But I guess this is answer probably differs from developer to developer.

JohanSellberg
  • 2,423
  • 1
  • 21
  • 28
  • 1
    I agree 100% that maintainability is more important than performance; and to me, smaller logs are more maintainable than larger. – jaco0646 Jan 17 '20 at 15:56
  • Smaller log means smaller information and why do you think its more maintainable? @jaco0646 – Sazzad Hissain Khan Jan 18 '20 at 10:41
  • @SazzadHissainKhan, in general, less of anything is more maintainable (especially less code). When it comes to logs, more logging is more noise, making it harder to find any information buried there. To say it another way, the signal-to-noise ratio in large logs is generally very poor. – jaco0646 Jan 21 '20 at 18:25
3

I would agree with other poster about traps of over designing for maintainability (or any other reason you foresee in future) - for me it rarely paid off, meaning I had to redesign some time in future anyway. I did use exception handle & throw in different layers when I worked on large enterprise application - monolith application with similarly tiered architecture, and application logs were nightmare to analyze.

I will assume that you are not using exceptions to control application flow, which is an anti pattern.

From my experience exception should be handled by responsible layer and the result returned according to the 'contract' with calling layers. This is makes very tight interface between layers and exception is thrown by lower layer when agreed interface is not being honored (e.g. IllegalArgument)

In case that handling of an exception can not result in valid returned result (eg. Database connection lost) then generic 'Layer Exception' can be thrown by layer handling the exception (eg. 'DataAccessError' thrown by Data Layer). This exception should only by caught by the layer that can return result to upper layers according to the contract, otherwise should not be handled. In the example ultimately Presentation layer will handle the exception gracefully.

Whichever layer is handling the exception it should also log it, and in you case you only need a way to distinguish in the logs from which layer log line is originating so for the above example log would look something like this...

[PRESENTATION] Error displaying Product Info: DataAccessLayerError - Error fetching Product info for ID 123.
...
...
[DATA] Error fetching Product info for ID 123:
/stack trace of caught DB Connection Error/

This coupled with printing Thread Context (or equivalent in your logging framework) will make it easier to trace errors in your log files.

Milan
  • 1,903
  • 17
  • 16
1

When you cannot recover just re-throw you exception and catch it in the interceptor layer (middleware).

Otherwise you can follow this interesting domain pattern: https://martinfowler.com/articles/domain-oriented-observability.html

John Smith
  • 1,091
  • 9
  • 17
1

You can try to follow this domain pattern https://martinfowler.com/articles/domain-oriented-observability.html Besides code which is not maintainable, cannot generally lead to a good performance increase.

Chris
  • 93
  • 1
  • 10
0

Ideally, the answer is the Business Logic Layer. As for the Persistence Layer exceptions can be caught at the Business Logic Layer.

Also, the Presentation Layer work is to take the data from the UI Layer, deserialize it and send it to the Business Logic Layer, then take the data from Business Logic Layer serialize it and send it to the UI Layer.

OR

It also depends on the architecture of the software. You can also log the error in every layer and use a unique identifier to look for a specific operation. For instance, software using HTTP requests should use a unique requestId in every log statement to identify all the operations for a request and for a Messaging (Queuing) System a messageId can be used similarly to identify the logs for an operation.

Asad Shakeel
  • 1,949
  • 1
  • 23
  • 29
0

The first thought that came to move the logging responsibility to all of your exceptions.

By making all of your Exceptions inherit form the same Base, which composes your system's logger, and can trigger the logging whenever it is being thrown.

That way, it doesn't matter which layer it was thrown in, what matters that you have made all exceptions responsible for their logging, since they have the most information required for logging. Check Information Expert Concept in GRASP

                  +----------------------+
                  |                      |
        +---------+   LoggableException  +----------+
        |         |                      |          |
        |         +-----------+----------+          |
        |                     |                     |
        |                     |                     |
        |                     |                     |
        |                     |                     |
+-------v--------+    +-------v--------+   +--------v--------+
|                |    |                |   |                 |
|  Exception1    |    |   Exception2   |   |   Exception3    |
+----------------+    +----------------+   +-----------------+

Radwan Abu-Odeh
  • 1,897
  • 9
  • 16
  • That wouldn’t solve the actual problem about performance and maintainability concerns. Still there will be duplicate logs with a cleaner codebase. – Sazzad Hissain Khan Jan 21 '20 at 10:35
  • When you want to change "status to loggable", you have to add and modify a class definition. Additionally this is only consumer responsibility to decide log or not to log an exception, not the class itself. Using interface as a some kind of marker - it's not a brilliant idea at all. – John Smith Feb 02 '20 at 13:23