Masuk Scala

168

Apa cara yang baik untuk melakukan login dalam aplikasi Scala? Sesuatu yang konsisten dengan filosofi bahasa, tidak mengacaukan kode, dan pemeliharaannya rendah dan tidak mengganggu. Berikut daftar persyaratan dasar:

  • sederhana
  • tidak mengacaukan kode. Scala sangat bagus untuk singkatnya. Saya tidak ingin setengah dari kode saya menjadi pernyataan logging
  • format log dapat diubah agar sesuai dengan sisa log perusahaan saya dan perangkat lunak pemantauan
  • mendukung level logging (mis. debug, jejak, kesalahan)
  • dapat masuk ke disk serta tujuan lain (mis. soket, konsol, dll.)
  • konfigurasi minimum, jika ada
  • bekerja dalam wadah (mis., server web)
  • (opsional, tetapi menyenangkan untuk dimiliki) hadir sebagai bagian dari bahasa atau sebagai artefak pakar, jadi saya tidak perlu meretas bangunan saya untuk menggunakannya

Saya tahu saya bisa menggunakan solusi logging Java yang ada, tetapi mereka gagal pada setidaknya dua di atas, yaitu kekacauan dan konfigurasi.

Terima kasih atas balasan Anda.

George
sumber

Jawaban:

119

pembungkus slf4j

Sebagian besar pustaka logging Scala memiliki beberapa pembungkus di sekitar kerangka kerja logging Java (slf4j, log4j dll), tetapi pada Maret 2015, pustaka log yang bertahan semua slf4j. Pustaka log ini menyediakan semacam logobjek yang dapat Anda panggil info(...), debug(...)dll. Saya bukan penggemar slf4j, tetapi sekarang tampaknya merupakan kerangka kerja logging yang dominan. Berikut deskripsi SLF4J :

Simple Logging Facade for Java atau (SLF4J) berfungsi sebagai fasad atau abstraksi sederhana untuk berbagai kerangka kerja logging, misalnya java.util.logging, log4j dan logback, memungkinkan pengguna akhir untuk memasukkan kerangka logging yang diinginkan pada waktu penempatan.

Kemampuan untuk mengubah pustaka log yang mendasarinya pada waktu penyebaran menghadirkan karakteristik unik bagi seluruh keluarga penebang slf4j, yang perlu Anda perhatikan:

  1. classpath sebagai pendekatan konfigurasi . Cara slf4j mengetahui pustaka logging mana yang Anda gunakan adalah dengan memuat kelas dengan beberapa nama. Saya punya masalah di mana slf4j tidak mengenali logger saya ketika classloader dikustomisasi.
  2. Karena fasad sederhana mencoba menjadi penyebut yang umum, ia hanya terbatas pada panggilan log yang sebenarnya. Dengan kata lain, konfigurasi tidak dapat dilakukan melalui kode.

Dalam proyek besar, sebenarnya bisa nyaman untuk dapat mengendalikan perilaku logging dari dependensi transitif jika semua orang menggunakan slf4j.

Penebangan Scala

Scala Logging ditulis oleh Heiko Seeberger sebagai penerus slf4s- nya . Ini menggunakan makro untuk memperluas panggilan ke ekspresi jika untuk menghindari panggilan log berpotensi mahal.

Scala Logging adalah perpustakaan pencatatan log yang nyaman dan performan yang membungkus perpustakaan logging seperti SLF4J dan yang lainnya.

Penebang historis

  • Logula , pembungkus Log4J yang ditulis oleh Coda Hale. Dulu suka yang ini, tapi sekarang sudah ditinggalkan.
  • configgy , pembungkus java.util.logging yang dulu populer di hari-hari awal Scala. Sekarang ditinggalkan.
Eugene Yokota
sumber
1
Harus berkata, saya suka Logula ... akhirnya seorang penebang yang tidak membuat saya ingin menusuk obeng di telingaku. Tanpa xml dan tanpa BS, hanya scala config pada saat startup
Arg
SLF4S Heiko Seeberger tampaknya tidak lagi dipertahankan. Saya membuat penargetan saya sendiri Scala 2.10 dan SLF4J terbaru. http://slf4s.org/
Matt Roberts
3
Logula ditinggalkan sesuai dengan README-nya di Github.
Erik Kaplun
Oncue Journal mirip dalam konsepnya dengan Scala Logging, tetapi pesan log dikirim ke aktor yang memanggil SLF4J dari kumpulan utas khusus. Memperdagangkan waktu CPU untuk latensi (yang jauh lebih baik). github.com/oncue/journal
Gary Coady
64

Dengan Scala 2.10+ Pertimbangkan ScalaLogging oleh Typesafe. Menggunakan makro untuk memberikan API yang sangat bersih

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

Mengutip dari wiki mereka:

Untungnya macro Scala dapat digunakan untuk membuat hidup kita lebih mudah: ScalaLogging menawarkan kelas Loggerdengan metode logging ringan yang akan diperluas ke idiom di atas. Jadi yang harus kita tulis adalah:

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

Setelah makro diterapkan, kode akan diubah menjadi idiom yang dijelaskan di atas.

Selain itu ScalaLogging menawarkan sifat Loggingyang dengan mudah memberikan Loggercontoh diinisialisasi dengan nama kelas yang dicampur menjadi:

import com.typesafe.scalalogging.slf4j.LazyLogging

class MyClass extends LazyLogging {
  logger.debug("This is very convenient ;-)")
}
fracca
sumber
12
Gunakan class MyClass with Logging.
Andrzej Jozwik
1
Harap dicatat bahwa menggunakan sifat-sifat login menyebabkan sifat tersebut memiliki nama yang sama untuk logger sebagai kelas yang dicampurkan ke dalamnya. Saya pikir Anda bisa mendapatkan logger secara manual daripada kenyamanan makro untuk memberikan nama logger secara manual, jika diperlukan. Tapi tolong, koreksi saya jika saya salah.
mkko
1
Apakah ini berarti MyClass memiliki metode umum untuk debug, info beri peringatan dll? Jika demikian saya lebih suka melakukan pekerjaan manual ekstra instantiating pribadi logger ke kelas. Metode logging seharusnya tidak bocor ke antarmuka kelas.
CHUCK
2
Anda mendapatkan bidang logger, yang dapat Anda gunakan di waktu luang Anda. Lihat github.com/typesafehub/scalalogging/blob/master/... untuk rincian
fracca
2
2.x membutuhkan Scala 2.11; semoga tidak ada banyak perbedaan praktis; untuk Scala 2.10.x, ada "com.typesafe" %% "scalalogging-slf4j" % "1.1.0".
Erik Kaplun
14

Menggunakan slf4j dan pembungkus bagus tetapi penggunaan itu dibangun di interpolasi rusak ketika Anda memiliki lebih dari dua nilai untuk interpolasi, sejak itu Anda perlu membuat Array nilai untuk interpolasi.

Solusi yang lebih mirip Scala adalah dengan menggunakan thunk atau cluster untuk menunda penyatuan pesan kesalahan. Contoh yang bagus untuk hal ini adalah Lift's logger

Log.scala Slf4jLog.scala

Yang terlihat seperti ini:

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

Perhatikan bahwa msg adalah nama panggilan dan tidak akan dievaluasi kecuali isTraceEnabled benar sehingga tidak ada biaya dalam menghasilkan string pesan yang bagus. Ini bekerja di sekitar mekanisme interpolasi slf4j yang membutuhkan parsing pesan kesalahan. Dengan model ini, Anda dapat menginterpolasi sejumlah nilai ke dalam pesan kesalahan.

Jika Anda memiliki sifat terpisah yang mencampur Log4JLogger ini ke dalam kelas Anda, maka Anda bisa melakukannya

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

dari pada

info("The foobar from {0} doesn't match the foobar from {1} and you should reset the baz from {c},
     Array(a, b, c))
Blair Zajac
sumber
2
apakah mungkin untuk mendapatkan nomor baris yang akurat untuk mencatat logging di kelas scala ?????
jpswain
13

Jangan gunakan Logula

Saya sebenarnya telah mengikuti rekomendasi dari Eugene dan mencobanya dan menemukan bahwa ia memiliki konfigurasi yang canggung dan mengalami bug, yang tidak diperbaiki (seperti ini ). Itu tidak terlihat terpelihara dengan baik dan tidak mendukung Scala 2.10 .

Gunakan slf4s + slf4j-simple

Kunci Keuntungan:

  • Mendukung Scala 2.10 terbaru (sampai saat ini M7)
  • Konfigurasi itu serbaguna tetapi tidak bisa lebih sederhana. Ini dilakukan dengan properti sistem , yang dapat Anda atur dengan menambahkan sesuatu seperti -Dorg.slf4j.simplelogger.defaultlog=traceke perintah eksekusi atau hardcode di skrip Anda:System.setProperty("org.slf4j.simplelogger.defaultlog", "trace") . Tidak perlu mengelola file konfigurasi sampah!
  • Cocok dengan IDE. Misalnya untuk mengatur tingkat penebangan untuk "jejak" dalam konfigurasi tertentu dijalankan di IDEA hanya pergi ke Run/Debug Configurationsdan menambahkan -Dorg.slf4j.simplelogger.defaultlog=traceke VM options.
  • Pengaturan mudah: cukup masukkan dependensi dari bawah jawaban ini

Inilah yang Anda perlukan untuk menjalankannya dengan 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>
Nikita Volkov
sumber
di mana Anda menemukan slf4 yang mendukung 2.10? Saya sedang melihat github.com/weiglewilczek/slf4s dan lihat "Versi Scala yang didukung 2.8.0, 2.8.1, 2.9.0-1 dan 2.9.1." .. Apakah saya mencari di tempat yang salah?
nairbv
@ Brian Gunakan 2.9.1 seperti yang disarankan dalam jawaban. Saya sudah mengujinya untuk bekerja dengan baik dengan 2.10.0-M7
Nikita Volkov
Saya menggunakan 2.9.1, saya hanya ingin tahu tentang "manfaat utama" yang disorot dan apa artinya.
nairbv
Setelah dengan hati-hati mencari jawaban untuk pro dan kontra dari perpustakaan logging, saya memilih salah satu yang memberitahu saya apa ketergantungan untuk mengimpor. Terima kasih tuan.
teo
11

Beginilah cara saya mendapatkan Scala Logging bekerja untuk saya:

Letakkan ini di build.sbt:

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

Kemudian, setelah melakukan sbt update, ini mencetak pesan log ramah:

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

Jika Anda menggunakan Play, tentu saja Anda hanya import play.api.Loggerdapat menulis pesan log:Logger.debug("Hi") .

Lihat dokumen untuk info lebih lanjut.

Matthias Braun
sumber
Hanya bekerja untuk saya setelah saya membuat log4j.propertiesdi dalam folder sumber daya proyek.
Nadjib Mami
7

Saya menarik sedikit pekerjaan dari Loggingsifat itu scalax, dan menciptakan sifat yang juga mengintegrasikan MessageFormat-basedperpustakaan.

Maka hal-hal seperti ini terlihat seperti ini:

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

Kami menyukai pendekatan sejauh ini.

Penerapan:

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))  
}
Tristan Juricek
sumber
Lucu, meskipun saya mengalami beberapa masalah untuk mendapatkan ini berfungsi - khususnya, output (pada konfigurasi standar slf4j) selalu terlihat seperti berikut ini, bukannya kelas yang benar-benar memperluas Loggable: 22 Jul 2011 3:02:17 AM redacted.util.Info $ class info INFO: Beberapa pesan ... Apakah ada kemungkinan Anda mengalami ini?
Tomer Gabel
6

Saya menggunakan SLF4J + Logback klasik dan menerapkannya seperti ini:

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

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

Maka Anda dapat menggunakannya mana yang lebih cocok dengan gaya Anda:

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

tetapi pendekatan ini tentu saja menggunakan instance logger per instance kelas.

Kristof Jozsa
sumber
4

Anda harus melihat pada perpustakaan scalax: http://scalax.scalaforge.org/ Di perpustakaan ini, ada sifat Logging, menggunakan sl4j sebagai backend. Dengan menggunakan sifat ini, Anda bisa login dengan mudah (cukup gunakan bidang logger di kelas yang mewarisi sifat itu).


sumber
3

Formulir cepat dan mudah.

Scala 2.10 dan lebih lama:

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

Dan build.sbt:

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

Scala 2.11+ dan yang lebih baru:

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

Dan build.sbt:

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

Setelah menggunakan slf4s dan logula untuk sementara waktu, saya menulis loglady, slf4j pembungkus sifat logging sederhana.

Ini menawarkan API yang mirip dengan perpustakaan logging Python, yang membuat kasus umum (string dasar, pemformatan sederhana) sepele dan menghindari memformat boilerplate.

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

dln
sumber
2

Saya merasa sangat nyaman menggunakan beberapa jenis java logger, sl4j misalnya, dengan pembungkus scala sederhana, yang membawa saya sintaksis seperti

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

object User with dsl.logging {

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

}

Menurut pendapat saya sangat berguna mixin dari kerangka kerja logging java terbukti dan sintaksis mewah scala.

Alex Povar
sumber
@ sschaef, oh, ya. Maaf. Saya hampir lupa. Itu adalah pertarungan hebat dengan masalah ini selama beberapa hari, tapi sepertinya ini benar-benar mustahil. Saya telah menggunakan #! ! "pesan info" sebagai gantinya. Saya akan mengedit edit jawaban saya.
Alex Povar