Scalaでのロギング


168

Scalaアプリケーションでロギングを行う良い方法は何ですか?言語哲学と一致し、コードが乱雑にならないもので、メンテナンスが少なく、邪魔にならないもの。基本的な要件のリストは次のとおりです。

  • 簡単な
  • コードを散らかしません。Scalaはその簡潔さの点で優れています。コードの半分をステートメントのロギングにしたくない
  • 残りのエンタープライズログと監視ソフトウェアに合わせてログ形式を変更できます
  • ロギングのレベルをサポート(つまり、デバッグ、トレース、エラー)
  • ディスクやその他の宛先(ソケット、コンソールなど)にログを記録できます
  • 最小構成(ある場合)
  • コンテナーで動作します(つまり、Webサーバー)
  • (オプションですが、使いやすい)言語の一部またはMavenアーティファクトのいずれかで提供されるため、ビルドをハックして使用する必要はありません

私は既存のJavaロギングソリューションを使用できることを知っていますが、それらは上記の少なくとも2つ、つまりクラッターと構成で失敗します。

お返事ありがとうございます。

回答:


119

slf4jラッパー

Scalaのロギングライブラリのほとんどは、Javaロギングフレームワーク(slf4j、log4jなど)のラッパーでしたが、2015年3月の時点で、残っているログライブラリはすべてslf4jです。これらのログ・ライブラリは、ある種の提供logあなたが呼び出すことができますするオブジェクトinfo(...)debug(...)など私SLF4Jの大ファンではないが、今で優勢なロギングフレームワークのようです。SLF4Jの説明は次のとおりです。

Simple Logging Facade for Javaまたは(SLF4J)は、さまざまなロギングフレームワーク(java.util.logging、log4j、logbackなど)のシンプルなファサードまたは抽象化として機能し、エンドユーザーがデプロイメント時に必要なロギングフレームワークをプラグインできるようにします。

デプロイメント時に基礎となるログライブラリを変更する機能は、slf4jファミリーのロガー全体に固有の特性をもたらします。

  1. 構成アプローチとしてのクラスパス。slf4jが使用しているロギングライブラリを認識する方法は、クラスを名前でロードすることです。クラスローダーをカスタマイズしたときに、slf4jがロガーを認識しないという問題がありました。
  2. シンプルなファサードは一般的な特徴を示すため、実際のログ呼び出しのみに限定されます。つまり、コードを介して構成を行うことはできません。

大規模なプロジェクトでは、誰もがslf4jを使用している場合、推移的な依存関係のロギング動作を制御できると実際に便利です。

Scalaロギング

Scala LoggingはHeiko Seebergerによって彼のslf4sの後継として書かれています。マクロを使用して呼び出しをif式に展開し、高額なログ呼び出しを回避します。

Scala Loggingは、SLF4Jなどの可能性のあるロギングライブラリをラップする、便利でパフォーマンスの高いロギングライブラリです。

歴史的ロガー

  • Coda Haleによって書かれたLog4JラッパーであるLogula。以前はこれが好きでしたが、今では捨てられました。
  • configgyは、Scalaの初期の頃はよく使われていたjava.util.loggingラッパーです。放棄されました。

1
お奨め、私はLogulaが大好きです...ついに、ドライバーを耳に刺したくないロガーです。XMLもBSもありません。起動時のScala設定のみ
Arg

Heiko SeebergerのSLF4Sは維持されなくなったようです。Scala 2.10と最新のSLF4Jをターゲットにして自分で作成しました。http://slf4s.org/
Matt Roberts 14

3
Logulaは、GithubのREADMEに従って破棄されました。
エリックカプルン2014年

Oncue JournalはScala Loggingと概念が似ていますが、ログメッセージは、専用スレッドプールからSLF4Jを呼び出すアクターに送信されます。CPU時間を(はるかに良い)レイテンシと交換します。github.com/oncue/journal
Gary Coady

64

Scala 2.10+では、タイプセーフによる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 2013

1
Loggingトレイトを使用すると、混合するクラスと同じ名前のロガーの名前がトレイトに含まれることに注意してください。必要に応じて、マクロの便宜の代わりに手動でロガーを取得して、ロガー名を手動で提供できると思います。しかし、私が間違っている場合は修正してください。
mkko 2013

1
これは、MyClassにデバッグ、情報警告などのパブリックメソッドがあることを意味しますか?もしそうなら、私はクラスにプライベートなロガーをインスタンス化する余分な手動の仕事をしたいです。ロギングメソッドがクラスインターフェイスにリークしないようにする必要があります。
ChucK 2013

2
余暇に使用できるロガーフィールドを取得します。詳細はgithub.com/typesafehub/scalalogging/blob/master/…を参照してください
fracca '25

2
2.xにはScala 2.11が必要です。うまくいけば、実用的な違いはあまりありません。Scala 2.10.xにはがあり"com.typesafe" %% "scalalogging-slf4j" % "1.1.0"ます。
エリックカプルン2014年

14

slf4jとラッパーを使用するのは良いことですが、補間する値が3つ以上ある場合、組み込みの補間を使用すると、補間する値の配列を作成する必要があるため、機能しなくなります。

よりScalaに似た解決策は、サンクまたはクラスターを使用して、エラーメッセージの連結を遅延させることです。この良い例は、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 '29年

13

Logulaを使用しない

私は実際にはユージンの勧告に従って、それを試してみました、それは不器用な構成を有し、(のような固定されませんバグ、に供されることが分かってきたこの1)。十分にメンテナンスされているようには見えず、Scala 2.10をサポートしいません

slf4s + slf4j-simpleを使用

主な利点:

  • 最新のScala 2.10をサポート (これまでのところM7)
  • 構成は多用途ですが、これ以上簡単にはできません。これはシステムプロパティで行われます。システムプロパティ-Dorg.slf4j.simplelogger.defaultlog=traceは、スクリプトの実行コマンドやハードコードなどに追加することで設定できますSystem.setProperty("org.slf4j.simplelogger.defaultlog", "trace")。くだらない設定ファイルを管理する必要はありません!
  • IDEとうまく適合します。たとえば、IDEAの特定の実行構成でログレベルを「トレース」に設定するには、に移動しRun/Debug Configurationsてに追加-Dorg.slf4j.simplelogger.defaultlog=traceVM 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を見ていて、「サポートされているScalaのバージョンは2.8.0、2.8.1、2.9.0-1および2.9.1です。」を参照してください。..間違った場所を探していますか?
nairbv 2012

@Brian回答で提案されている2.9.1を使用します。2.10.0-M7で正常に動作することをテストしました
Nikita Volkov

私は2.9.1を使用していますが、強調表示された「主な利点」とその意味について興味がありました。
nairbv 2012

ロギングライブラリの長所と短所の回答を注意深く検索した後、インポートする依存関係を示すものを選択します。ありがとうございます。
teo

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特性から少し作業を引き出してscalaxMessageFormat-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 2011

6

私はSLF4J + Logbackクラシックを使用し、次のように適用します。

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を使用するLoggingトレイトがあります。このトレイトを使用すると、非常に簡単にログを記録できます(このトレイトを継承するクラスでロガーフィールドを使用するだけです)。




3

すばやく簡単なフォーム。

Scala 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"

Scala 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を使用した後、slf4jをlogladyラップする単純なロギングトレイトを書きました。

Pythonのロギングライブラリと同様のAPIを提供します。これにより、一般的なケース(基本的な文字列、単純なフォーマット)が簡単になり、ボイラープレートのフォーマットが回避されます。

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


2

ある種のJavaロガー、たとえばsl4jを使用して、単純なscalaラッパーを使用すると非常に便利です。

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

object User with dsl.logging {

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

}

私の意見では、Javaで実証済みのロギングフレームワークとScalaの派手な構文の非常に便利なミックスインです。


@sschaef、ああ、そうです。ごめんなさい。ほとんど忘れてしまいました。数日間、この問題との素晴らしい戦いでしたが、これは本当に不可能のようです。#を使用しました!!代わりに「情報メッセージ」。回答を編集して編集します。
Alex Povar 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.