스칼라 애플리케이션에서 로깅을 수행하는 좋은 방법은 무엇입니까? 언어 철학과 일치하고 코드를 어지럽히 지 않으며 유지 보수가 적고 눈에 거슬리지 않는 것이 있습니다. 기본 요구 사항 목록은 다음과 같습니다.
- 단순한
- 코드를 어지럽히 지 않습니다. 스칼라는 간결함이 뛰어납니다. 내 코드의 절반이 문장을 로깅하는 것을 원하지 않습니다.
- 나머지 엔터프라이즈 로그 및 모니터링 소프트웨어에 맞게 로그 형식을 변경할 수 있습니다.
- 로깅 수준 지원 (예 : 디버그, 추적, 오류)
- 다른 대상뿐만 아니라 디스크에 기록 할 수 있습니다 (예 : 소켓, 콘솔 등)
- 최소 구성 (있는 경우)
- 컨테이너 (예 : 웹 서버)에서 작동
- (선택적이지만 좋은) 언어의 일부 또는 메이븐 인공물로 제공되므로 사용하기 위해 빌드를 해킹 할 필요가 없습니다.
기존 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 로거 제품군에 고유 한 특성을 가져옵니다.
- 구성 방식 으로서의 클래스 경로 . slf4j가 사용중인 기본 로깅 라이브러리를 아는 방법은 이름으로 클래스를로드하는 것입니다. 클래스 로더를 사용자 정의 할 때 slf4j가 로거를 인식하지 못하는 문제가 있습니다.
- 단순한 파사드 는 공통 분모가 되려고하기 때문에 실제 로그 호출로만 제한됩니다. 즉, 코드를 통해 구성을 수행 할 수 없습니다.
대규모 프로젝트에서는 모든 사람이 slf4j를 사용하는 경우 전이 종속성의 로깅 동작을 제어하는 것이 실제로 편리 할 수 있습니다.
스칼라 로깅
Scala Logging 은 Heiko Seeberger가 자신의 slf4s 의 후계자로 작성했습니다 . 매크로를 사용하여 잠재적으로 비싼 로그 호출을 피하기 위해 if 표현식으로 호출을 확장합니다.
스칼라 로깅은 SLF4J 및 잠재적으로 다른 것들과 같은 로깅 라이브러리를 감싸는 편리하고 성능이 뛰어난 로깅 라이브러리입니다.
역사 로거
답변
Scala 2.10 이상에서는 Typesafe에 의한 ScalaLogging을 고려하십시오. 매크로를 사용하여 매우 깨끗한 API 제공
https://github.com/typesafehub/scala-logging
그들의 위키에서 인용 :
다행히 Scala 매크로를 사용하여 우리의 삶을 편하게 만들 수 있습니다. ScalaLogging은
Logger
위의 관용구로 확장되는 가벼운 로깅 방법을 제공합니다 . 우리가 작성해야 할 것은 다음과 같습니다.
logger.debug(s"Some ${expensiveExpression} message!")
매크로가 적용된 후에는 코드가 위에서 설명한 관용구로 변환됩니다.
또한 ScalaLogging은 클래스 이름으로 초기화 Logging
된 Logger
인스턴스 를 편리하게 제공하는 특성 을 제공합니다 .
import com.typesafe.scalalogging.slf4j.LazyLogging
class MyClass extends LazyLogging {
logger.debug("This is very convenient ;-)")
}
답변
slf4j와 래퍼를 사용하는 것은 좋지만 보간해야 할 값이 두 개 이상인 경우 보간에 내장 된 보간이 사용되어 고장납니다. 그러므로 보간 할 값 배열을 만들어야합니다.
스칼라와 같은 솔루션은 썽크 또는 클러스터를 사용하여 오류 메시지의 연결을 지연시키는 것입니다. 좋은 예는 Lift의 로거입니다.
이것은 다음과 같습니다
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")
}
그러나이 접근법은 물론 클래스 인스턴스마다 로거 인스턴스를 사용합니다.