스칼라에 로그인


168

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

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

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

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

답변:


119

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 래퍼. 이제 버려졌습니다.

1
나는 Logula를 좋아합니다 ... 마침내 로거를 찌르지 않으려는 로거입니다. xml도없고 BS도없고 시작시 스칼라 설정
Arg

Heiko Seeberger의 SLF4S는 더 이상 유지되지 않는 것 같습니다. Scala 2.10과 최신 SLF4J를 직접 타겟팅했습니다. http://slf4s.org/
Matt Roberts

3
Logula는 Github의 README에 따라 폐기됩니다.
Erik Kaplun

Oncue Journal은 개념 상 Scala Logging과 유사하지만 로그 메시지는 전용 스레드 풀에서 SLF4J를 호출하는 액터로 전송됩니다. CPU 시간을 훨씬 더 나은 대기 시간으로 교환합니다. github.com/oncue/journal
게리 코디 (Gary Coady)

64

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 ;-)")
}

12
사용하십시오 class MyClass with Logging.
Andrzej Jozwik

1
특성에 로깅을 사용하면 특성이 로거의 이름과 혼합 된 클래스와 동일한 이름이됩니다. 필요한 경우 매크로 편의 대신 로거를 수동으로 가져 와서 로거 이름을 수동으로 제공 할 수 있다고 생각합니다. 하지만 내가 틀렸다면 정정하십시오.
mkko

1
이것은 MyClass에 디버그, 정보 경고 등에 대한 공개 메소드가 있다는 것을 의미합니까? 그렇다면 클래스에 개인 로거를 인스턴스화하는 추가 수동 작업을 수행하고 싶습니다. 로깅 메소드는 클래스 인터페이스로 누출되지 않아야합니다.
ChucK

2
당신은 당신이 여가 시간에 사용할 수있는 로거 필드를 얻을 수 있습니다. 자세한 내용은 github.com/typesafehub/scalalogging/blob/master/… 를 참조하십시오.
fracca

2
2.x에는 스칼라 2.11이 필요합니다. 많은 실질적인 차이가 없기를 바랍니다. Scala 2.10.x에는 "com.typesafe" %% "scalalogging-slf4j" % "1.1.0".
Erik Kaplun

14

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))

2
스칼라 클래스의 명령문을 로깅하기 위해 정확한 줄 번호를 얻을 수 있습니까 ?????
jpswain

13

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>

2.10을 지원하는 slf4는 어디에 있습니까? 내가 찾고 있어요 github.com/weiglewilczek/slf4s 및 페이지의 "지원되는 스칼라 버전 2.8.0, 2.8.1, 2.9.0-1 및 2.9.1입니다." .. 내가 잘못된 장소를보고 있습니까?
nairbv

@Brian 답에 제안 된대로 2.9.1을 사용하십시오. 나는 2.10.0-M7 작업 괜찮을를 테스트 한
니키타 볼코프

2.9.1을 사용하고 있는데 강조 표시된 "핵심 이점"과 그 의미에 대해 궁금했습니다.
nairbv

로깅 라이브러리의 장단점에 대한 답변을 신중하게 검색 한 후 가져올 종속성을 알려주는 것을 선택합니다. 고마워요
teo December

11

이것이 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").

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


log4j.properties프로젝트 리소스 폴더 내부를 만든 후에 만 ​​나를 위해 일했습니다 .
Nadjib Mami

7

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 구성)은 실제로 Loggable을 확장하는 클래스 대신 항상 다음과 같습니다. Jul 22, 2011 3:02:17 AM redacted.util.Loggable $ class info 정보 : 일부 메시지 ...이 문제가 발생했을 가능성이 있습니까?
Tomer Gabel

6

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")
}

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


4

scalax 라이브러리를 살펴 봐야합니다. http://scalax.scalaforge.org/ 이 라이브러리에는 sl4j를 백엔드로 사용하는 로깅 특성이 있습니다. 이 특성을 사용하면 아주 쉽게 로깅 할 수 있습니다 (특성을 상속하는 클래스의 로거 필드 만 사용).




3

빠르고 쉬운 형태.

스칼라 2.10 이상 :

import com.typesafe.scalalogging.slf4j.Logger
import org.slf4j.LoggerFactory
val logger = Logger(LoggerFactory.getLogger("TheLoggerName"))
logger.debug("Useful message....")

그리고 build.sbt :

libraryDependencies += "com.typesafe" %% "scalalogging-slf4j" % "1.1.0"

스칼라 2.11 이상 :

import import com.typesafe.scalalogging.Logger
import org.slf4j.LoggerFactory
val logger = Logger(LoggerFactory.getLogger("TheLoggerName"))
logger.debug("Useful message....")

그리고 build.sbt :

libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0"

2

slf4s와 logula를 잠시 사용한 후, 나는 썼다. loglady 감싸는 간단한 로깅 특성을 썼습니다.

Python의 로깅 라이브러리와 유사한 API를 제공하므로 일반적인 경우 (기본 문자열, 간단한 서식)를 사소하게 만들고 상용구 형식을 피할 수 있습니다.

http://github.com/dln/loglady/


2

간단한 scala 래퍼와 같은 일종의 Java 로거, sl4j를 사용하면 매우 편리합니다.

val #! = new Logger(..) // somewhere deep in dsl.logging.

object User with dsl.logging {

  #! ! "info message"
  #! dbg "debug message"
  #! trace "var a=true"

}

제 생각에는 자바에서 입증 된 로깅 프레임 워크와 스칼라의 멋진 구문이 매우 유용합니다.


@sschaef, 아, 그래 죄송합니다. 나는 거의 잊었다. 며칠 동안이 문제와 큰 싸움 이었지만 실제로 불가능한 것처럼 보입니다. #을 사용했습니다! ! 대신 "정보 메시지". 답변 편집을 편집하겠습니다.
Alex Povar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.