提问人:Sazzad Hissain Khan 提问时间:1/12/2020 最后编辑:Sazzad Hissain Khan 更新时间:1/21/2020 访问量:1894
应记录哪些层的异常?
Which layers should be logging for exceptions?
问:
我有一个大型单体应用程序,有四层用于满足特定的功能要求。
UI Layer
-> Presentation Logic Layer
-> Business Logic Layer
-> Persistent Layer
呼叫流的一个最小工作示例可以是这样的,
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())
}
}
现在,如果在流程的任何一层(即)中发生任何异常,我决定使用适当的 TAG 和信息将其记录在每一层中,然后扔回上层进行传播,以便在调试时,每一层都可以使用适当的 TAG 过滤自己的日志,而无需查看其他层(即)。query exception in Database layer
especially when different teams are responsible for different layers
在审查时,我的一位同事评论说,在我的设计中,单个异常/错误的日志会重复,这可能会降低性能和内存。他的建议是将日志记录应用于特定异常的其中一个层(即 )。但是,他建议继续将例外抛给上层。query exception in Persistent Layer only
为了性能和内存,是否应该更改提供更好可维护性的日志记录方法?处理这种情况的一般建议是什么?
答:
答案很可能是可怕的。这要视情况而定。我的意思是,如果你有性能或内存方面的问题,那么每一点都会有所帮助。此外,重复的日志条目可能会带来其他问题(例如每个团队都会查看日志条目/错误,即使它与他们无关。花时间查看不相关的日志条目可能会浪费团队的宝贵时间,即使他们能够很快看到标记)。如果这是一个问题,则仅在源中记录它可能是一件好事。
也就是说,可维护性等也是积极的因素,应该特别考虑那些最有可能长期存在的大型单体应用。我曾多次陷入让事情变得过于复杂的陷阱,希望构建完美的解决方案,但增加的复杂性使它难以维持,以至于它产生了相反的效果,即非常糟糕。
因此,我的建议是,如果当前没有内存、性能等方面的问题,那么可维护性将胜过长期的单体应用程序。但我想这个答案可能因开发人员而异。
评论
当您无法恢复时,只需重新抛出异常并在拦截器层(中间件)中捕获它。
否则,您可以遵循以下有趣的域模式:https://martinfowler.com/articles/domain-oriented-observability.html
我同意其他关于过度设计可维护性陷阱的陷阱(或你预见到的任何其他原因)——对我来说,它很少得到回报,这意味着我不得不在未来的某个时候重新设计。当我在处理大型企业应用程序时,我确实在不同的层中使用了异常句柄和抛出 - 具有类似分层架构的单体应用程序,应用程序日志分析起来是噩梦。
我假设您没有使用异常来控制应用程序流,这是一种反模式。
根据我的经验,异常应该由负责层处理,并根据调用层的“契约”返回结果。这使得层之间的接口非常紧密,当商定的接口不被遵守时,下层会抛出异常(例如 IllegalArgument)
如果处理异常不能产生有效的返回结果(例如。数据库连接丢失),则处理异常的层可以抛出通用的“层异常”(例如。数据层抛出的“DataAccessError”)。这个异常只能由能够根据合约将结果返回给上层的层捕获,否则不应处理。在示例中,最终表示层将优雅地处理异常。
无论哪个层正在处理异常,它都应该记录它,在这种情况下,您只需要一种方法来区分日志中哪个层日志行来自哪个层,因此对于上面的示例,日志将如下所示......
[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/
这与打印线程上下文(或日志记录框架中的等效项)相结合,可以更轻松地跟踪日志文件中的错误。
您可以尝试遵循此域模式 https://martinfowler.com/articles/domain-oriented-observability.html 除了代码不可维护之外,通常不能带来良好的性能提升。
理想情况下,答案是 .至于例外情况,可以在 .Business Logic Layer
Persistence Layer
Business Logic Layer
另外,工作是从 中获取数据,将其反序列化并发送到 ,然后从序列化中获取数据并将其发送到 .Presentation Layer
UI Layer
Business Logic Layer
Business Logic Layer
UI Layer
或
这也取决于软件的架构。您还可以在每一层中记录错误,并使用唯一标识符来查找特定操作。例如,使用 HTTP 请求的软件应在每个日志语句中使用唯一的 a 来标识请求的所有操作,而对于消息传递(队列)系统,可以类似地使用消息传递(队列)系统来标识操作的日志。requestId
messageId
第一个想法是将日志记录责任转移到所有异常。
通过使所有异常继承同一个 Base,它组成了系统的记录器,并且可以在抛出日志记录时触发日志记录。
这样一来,它被扔进哪一层并不重要,重要的是你让所有异常都负责它们的日志记录,因为它们拥有日志记录所需的最多信息。在GRASP中检查信息专家概念
+----------------------+
| |
+---------+ LoggableException +----------+
| | | |
| +-----------+----------+ |
| | |
| | |
| | |
| | |
+-------v--------+ +-------v--------+ +--------v--------+
| | | | | |
| Exception1 | | Exception2 | | Exception3 |
+----------------+ +----------------+ +-----------------+
评论
上一个:通过构造函数或方法添加参数
评论