2016-07-22 12 views
1

我们目前正在使用Play框架,并使用标准的日志记录机制。我们已经实现了一个隐式上下文来支持将用户名和会话ID传递给所有的服务方法。我们想要实现日志记录,以便它基于会话。这需要实施我们自己的记录器。这适用于我们自己的日志,但是我们如何为基本的异常处理和日志做相同的处理。也许有更好的方法来捕获这个问题,然后我们可以重写异常处理日志。本质上,我们希望获得与会话关联的尽可能多的日志消息。如何在Play框架中执行基于会话的日志记录

回答

0

这取决于如果你正在做反应式的发展或标准同步发展:

  • 如果标准同步发展(即没有期货,每个请求1线) - 那么我建议你只使用MDC,这将值添加到Threadlocal进行记录。然后您可以在logback/log4j中定制输出。当你获得用户名/会话(可能在过滤器或控制器中)时,你可以在那里设置值,然后你不需要用implicits来传递它们。

如果你在做无功的发展,你有两个选择:

  • ,您仍然可以使用MDC,但你必须使用自定义的执行上下文,有效地复制MDC值的线程,因为每个请求在理论上可以由多个线程处理。 (如此处所述:http://code.hootsuite.com/logging-contextual-info-in-an-asynchronous-scala-application/

  • 另一种方法是我倾向于使用的解决方案(并且接近您现在拥有的解决方案):您可以创建一个代表MyAppRequest的类。设置用户名,会话信息和其他内容。您可以继续以隐式方式传递它。然而,而不是使用Action.async,你让其中像下面

    myAction.async {隐含myRequest => //一些代码}

    的myAction里面,你必须用你自己的MyAction类捕获所有异常并处理将来的失败,并手动执行错误处理,而不是依赖ErrorHandler。我经常将myAction注入到控制器中,并在其中添加通用过滤器功能。

    这是不利的一面,它只是一种手动方法。此外,我还让MyAppRequest拥有可在任何位置设置的可记录值的映射,这意味着它必须是可变映射。另外,有时您需要创建多个myAction.async。亲是,它是相当明确的,并在您的控制没有太多的ExecutionContext/ThreadLocal魔术。

下面是一些非常粗略的示例代码作为起动,对于手动解决方案:

def logErrorAndRethrow(myrequest:MyRequest, x:Throwable): Nothing = { 
    //log your error here in the format you like 
    throw x //you can do this or handle errors how you like 
} 

class MyRequest { 
    val attr : mutable.Map[String, String] = new mutable.HashMap[String, String]() 
} 

//make this a util to inject, or move it into a common parent controller 
def myAsync(block: MyRequest => Future[Result]): Action[AnyContent] = { 
    val myRequest = new MyRequest() 
    try { 
    Action.async(
     block(myRequest).recover { case cause => logErrorAndRethrow(myRequest, cause) } 
    ) 
    } catch { 
    case x:Throwable => 
     logErrorAndRethrow(myRequest, x) 
    } 
} 

//the method your Route file refers to 
def getStuff = myAsync { request:MyRequest => 
    //execute your code here, passing around request as an implicit 
    Future.successful(Results.Ok) 
} 
相关问题