[logging] 스칼라에 로그인

스칼라 애플리케이션에서 로깅을 수행하는 좋은 방법은 무엇입니까? 언어 철학과 일치하고 코드를 어지럽히 지 않으며 유지 보수가 적고 눈에 거슬리지 않는 것이 있습니다. 기본 요구 사항 목록은 다음과 같습니다.

  • 단순한
  • 코드를 어지럽히 지 않습니다. 스칼라는 간결함이 뛰어납니다. 내 코드의 절반이 문장을 로깅하는 것을 원하지 않습니다.
  • 나머지 엔터프라이즈 로그 및 모니터링 소프트웨어에 맞게 로그 형식을 변경할 수 있습니다.
  • 로깅 수준 지원 (예 : 디버그, 추적, 오류)
  • 다른 대상뿐만 아니라 디스크에 기록 할 수 있습니다 (예 : 소켓, 콘솔 등)
  • 최소 구성 (있는 경우)
  • 컨테이너 (예 : 웹 서버)에서 작동
  • (선택적이지만 좋은) 언어의 일부 또는 메이븐 인공물로 제공되므로 사용하기 위해 빌드를 해킹 할 필요가 없습니다.

기존 Java 로깅 솔루션을 사용할 수 있지만 위의 두 가지, 즉 혼란과 구성에서 실패합니다.

답장을 보내 주셔서 감사합니다.



답변

slf4j 포장지

스칼라의 대부분의 로깅 라이브러리는 Java 로깅 프레임 워크 (slf4j, log4j 등)를 감싸는 래퍼이지만 2015 년 3 월 현재 남아있는 로그 라이브러리는 모두 slf4j입니다. 이러한 로그 라이브러리는 일종의 제공 log호출 할 수있는에 개체 info(...), debug(...)등 내가 SLF4J의 큰 팬이 아니에요,하지만 지금은 주된 로깅 프레임 워크가 될 것으로 보인다. SLF4J에 대한 설명은 다음과 같습니다 .

Java 또는 SLF4J (Simple Logging Facade for Java)는 다양한 로깅 프레임 워크 (예 : java.util.logging, log4j 및 logback)에 대한 간단한 외관 또는 추상화 역할을하여 최종 사용자가 배치시 원하는 로깅 프레임 워크를 플러그인 할 수 있도록합니다.

배치시 기본 로그 라이브러리를 변경하는 기능은 전체 slf4j 로거 제품군에 고유 한 특성을 가져옵니다.

  1. 구성 방식 으로서의 클래스 경로 . slf4j가 사용중인 기본 로깅 라이브러리를 아는 방법은 이름으로 클래스를로드하는 것입니다. 클래스 로더를 사용자 정의 할 때 slf4j가 로거를 인식하지 못하는 문제가 있습니다.
  2. 단순한 파사드 는 공통 분모가 되려고하기 때문에 실제 로그 호출로만 제한됩니다. 즉, 코드를 통해 구성을 수행 할 수 없습니다.

대규모 프로젝트에서는 모든 사람이 slf4j를 사용하는 경우 전이 종속성의 로깅 동작을 제어하는 ​​것이 실제로 편리 할 수 ​​있습니다.

스칼라 로깅

Scala Logging 은 Heiko Seeberger가 자신의 slf4s 의 후계자로 작성했습니다 . 매크로를 사용하여 잠재적으로 비싼 로그 호출을 피하기 위해 if 표현식으로 호출을 확장합니다.

스칼라 로깅은 SLF4J 및 잠재적으로 다른 것들과 같은 로깅 라이브러리를 감싸는 편리하고 성능이 뛰어난 로깅 라이브러리입니다.

역사 로거

  • Logula , Coda Hale이 작성한 Log4J 래퍼. 이것을 좋아했지만 이제는 버렸습니다.
  • configgy , 스칼라 초기에 널리 사용되었던 java.util.logging 래퍼. 이제 버려졌습니다.

답변

Scala 2.10 이상에서는 Typesafe에 의한 ScalaLogging을 고려하십시오. 매크로를 사용하여 매우 깨끗한 API 제공

https://github.com/typesafehub/scala-logging

그들의 위키에서 인용 :

다행히 Scala 매크로를 사용하여 우리의 삶을 편하게 만들 수 있습니다. ScalaLogging은 Logger위의 관용구로 확장되는 가벼운 로깅 방법을 제공합니다 . 우리가 작성해야 할 것은 다음과 같습니다.

logger.debug(s"Some ${expensiveExpression} message!")

매크로가 적용된 후에는 코드가 위에서 설명한 관용구로 변환됩니다.

또한 ScalaLogging은 클래스 이름으로 초기화 LoggingLogger인스턴스 를 편리하게 제공하는 특성 을 제공합니다 .

import com.typesafe.scalalogging.slf4j.LazyLogging

class MyClass extends LazyLogging {
  logger.debug("This is very convenient ;-)")
}


답변

slf4j와 래퍼를 사용하는 것은 좋지만 보간해야 할 값이 두 개 이상인 경우 보간에 내장 된 보간이 사용되어 고장납니다. 그러므로 보간 할 값 배열을 만들어야합니다.

스칼라와 같은 솔루션은 썽크 또는 클러스터를 사용하여 오류 메시지의 연결을 지연시키는 것입니다. 좋은 예는 Lift의 로거입니다.

Log.scala
Slf4jLog.scala

이것은 다음과 같습니다

class Log4JLogger(val logger: Logger) extends LiftLogger {
  override def trace(msg: => AnyRef) = if (isTraceEnabled) logger.trace(msg)
}

msg는 이름 별 호출이며 isTraceEnabled가 true가 아니면 평가되지 않으므로 멋진 메시지 문자열을 생성하는 데 비용이 들지 않습니다. 이 오류 메시지를 구문 분석해야 slf4j의 보간 메커니즘을 해결합니다. 이 모델을 사용하면 여러 값을 오류 메시지에 보간 할 수 있습니다.

이 Log4JLogger를 클래스에 혼합하는 별도의 특성이 있다면 할 수 있습니다

trace("The foobar from " + a + " doesn't match the foobar from " +
      b + " and you should reset the baz from " + c")

대신에

info("The foobar from {0} doesn't match the foobar from {1} and you should reset the baz from {c},
     Array(a, b, c))


답변

Logula를 사용하지 마십시오

실제로 Eugene의 권장 사항을 따르고 시도하여 서투른 구성을 가지고 있으며 수정되지 않는 버그가 있음을 알았습니다 (예 : 것과 같은 ). 잘 관리되지 않은 것으로 보이며 Scala 2.10을 지원하지 않습니다. .

slf4s + slf4j-simple 사용

주요 혜택:

  • 최신 스칼라 2.10 지원 (현재 M7)
  • 구성은 다양하지만 더 단순 할 수는 없습니다. 그것은 시스템 속성으로 이루어지며 , -Dorg.slf4j.simplelogger.defaultlog=trace스크립트에서 실행 명령이나 하드 코드 와 같은 것을 추가하여 설정할 수 있습니다System.setProperty("org.slf4j.simplelogger.defaultlog", "trace") . 쓰레기 설정 파일을 관리 할 필요가 없습니다!
  • IDE와 잘 어울립니다. 예를 들어 IDEA의 특정 실행 구성에서 로깅 수준을 “추적”으로 설정하려면 다음으로 이동하여 Run/Debug Configurations추가하십시오 -Dorg.slf4j.simplelogger.defaultlog=trace.VM options .
  • 쉬운 설정 :이 답변의 맨 아래에서 종속성을 삭제하십시오.

Maven으로 실행해야 할 사항은 다음과 같습니다.

<dependency>
  <groupId>com.weiglewilczek.slf4s</groupId>
  <artifactId>slf4s_2.9.1</artifactId>
  <version>1.0.7</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>1.6.6</version>
</dependency>


답변

이것이 Scala Logging을 작동시키는 방법입니다.

이것을 당신의 것으로 넣으십시오 build.sbt:

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.7.2",
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"

그런 다음을 수행하면 sbt update친숙한 로그 메시지가 인쇄됩니다.

import com.typesafe.scalalogging._
object MyApp extends App with LazyLogging {
  logger.info("Hello there")
}

Play를 사용하는 경우, 간단히 import play.api.Logger로그 메시지를 작성하기 만하면 됩니다 : Logger.debug("Hi").

자세한 내용은 문서 를 참조하십시오.


답변

Logging특성 에서 약간의 작업을 가져 와서 라이브러리를 scalax통합하는 특성을 만들었습니다 MessageFormat-based.

그런 다음 물건의 모양은 다음과 같습니다.

class Foo extends Loggable {
    info( "Dude, I'm an {0} with {1,number,#}", "Log message", 1234 )
}

우리는 지금까지의 접근법을 좋아합니다.

이행:

trait Loggable {

    val logger:Logger = Logging.getLogger(this)

    def checkFormat(msg:String, refs:Seq[Any]):String =
        if (refs.size > 0) msgfmtSeq(msg, refs) else msg

    def trace(msg:String, refs:Any*) = logger trace checkFormat(msg, refs)

    def trace(t:Throwable, msg:String, refs:Any*) = logger trace (checkFormat(msg, refs), t)

    def info(msg:String, refs:Any*) = logger info checkFormat(msg, refs)

    def info(t:Throwable, msg:String, refs:Any*) = logger info (checkFormat(msg, refs), t)

    def warn(msg:String, refs:Any*) = logger warn checkFormat(msg, refs)

    def warn(t:Throwable, msg:String, refs:Any*) = logger warn (checkFormat(msg, refs), t)

    def critical(msg:String, refs:Any*) = logger error checkFormat(msg, refs)

    def critical(t:Throwable, msg:String, refs:Any*) = logger error (checkFormat(msg, refs), t)

}

/**
 * Note: implementation taken from scalax.logging API
 */
object Logging {

    def loggerNameForClass(className: String) = {
        if (className endsWith "$") className.substring(0, className.length - 1)
        else className
    }

    def getLogger(logging: AnyRef) = LoggerFactory.getLogger(loggerNameForClass(logging.getClass.getName))
}


답변

SLF4J + Logback classic을 사용하여 다음과 같이 적용합니다.

trait Logging {
  lazy val logger = LoggerFactory.getLogger(getClass)

  implicit def logging2Logger(anything: Logging): Logger = anything.logger
}

그런 다음 스타일에 맞는 것을 사용할 수 있습니다.

class X with Logging {
    logger.debug("foo")
    debug("bar")
}

그러나이 접근법은 물론 클래스 인스턴스마다 로거 인스턴스를 사용합니다.