Đăng nhập vào Scala


168

Cách tốt để đăng nhập vào ứng dụng Scala là gì? Một cái gì đó phù hợp với triết lý ngôn ngữ, không làm lộn xộn mã, và ít bảo trì và không phô trương. Đây là danh sách yêu cầu cơ bản:

  • đơn giản
  • không làm lộn xộn mã. Scala là tuyệt vời cho sự ngắn gọn của nó. Tôi không muốn một nửa mã của mình được ghi nhật ký
  • định dạng nhật ký có thể được thay đổi để phù hợp với phần còn lại của nhật ký doanh nghiệp và phần mềm giám sát của tôi
  • hỗ trợ các mức ghi nhật ký (tức là gỡ lỗi, theo dõi, lỗi)
  • có thể đăng nhập vào đĩa cũng như các điểm đến khác (ví dụ: socket, console, v.v.)
  • cấu hình tối thiểu, nếu có
  • hoạt động trong các thùng chứa (tức là máy chủ web)
  • (tùy chọn, nhưng rất tốt để có) là một phần của ngôn ngữ hoặc là một tạo tác maven, vì vậy tôi không phải hack các bản dựng của mình để sử dụng nó

Tôi biết tôi có thể sử dụng các giải pháp ghi nhật ký Java hiện có, nhưng chúng thất bại ở ít nhất hai trong số trên, đó là sự lộn xộn và cấu hình.

Cảm ơn bạn đã trả lời.

Câu trả lời:


119

giấy gói slf4j

Hầu hết các thư viện ghi nhật ký của Scala là một số trình bao bọc xung quanh khung ghi nhật ký Java (slf4j, log4j, v.v.), nhưng kể từ tháng 3 năm 2015, các thư viện nhật ký còn tồn tại đều là slf4j. Các thư viện nhật ký này cung cấp một số loại logđối tượng mà bạn có thể gọi info(...), debug(...)v.v. Tôi không phải là một fan hâm mộ lớn của slf4j, nhưng giờ đây nó dường như là khung ghi nhật ký chiếm ưu thế. Dưới đây là mô tả của SLF4J :

Mặt tiền ghi nhật ký đơn giản cho Java hoặc (SLF4J) đóng vai trò là mặt tiền đơn giản hoặc trừu tượng hóa cho các khung ghi nhật ký khác nhau, ví dụ java.util.logging, log4j và logback, cho phép người dùng cuối cắm vào khung ghi nhật ký mong muốn khi triển khai.

Khả năng thay đổi thư viện nhật ký cơ bản tại thời điểm triển khai mang lại đặc tính duy nhất cho toàn bộ họ logger slf4j, mà bạn cần lưu ý:

  1. classpath như cách tiếp cận cấu hình . Cách slf4j biết thư viện ghi nhật ký cơ bản nào bạn đang sử dụng bằng cách tải một lớp theo tên nào đó. Tôi đã có vấn đề trong đó slf4j không nhận ra logger của tôi khi trình nạp lớp được tùy chỉnh.
  2. Bởi vì mặt tiền đơn giản cố gắng trở thành mẫu số chung, nên nó chỉ giới hạn ở các cuộc gọi nhật ký thực tế. Nói cách khác, cấu hình không thể được thực hiện thông qua mã.

Trong một dự án lớn, thực sự có thể thuận tiện để có thể kiểm soát hành vi ghi nhật ký của các phụ thuộc bắc cầu nếu mọi người sử dụng slf4j.

Ghi nhật ký Scala

Scala Logging được viết bởi Heiko Seeberger như một sự kế thừa cho slf4s của mình . Nó sử dụng macro để mở rộng các cuộc gọi thành biểu thức if để tránh cuộc gọi log đắt tiền.

Scala Logging là một thư viện ghi nhật ký tiện lợi và hiệu quả bao bọc các thư viện ghi nhật ký như SLF4J và các tiềm năng khác.

Khai thác lịch sử

  • Logula , một trình bao bọc Log4J được viết bởi Coda Hale. Đã từng thích cái này, nhưng giờ nó đã bị bỏ rơi.
  • configgy , một trình bao bọc java.util.logging đã từng phổ biến trong những ngày đầu của Scala. Bây giờ bị bỏ rơi.

1
Phải nói rằng, tôi yêu Logula ... cuối cùng là một logger không khiến tôi muốn vặn tua vít lên tai. Không có xml và không có BS, chỉ là một cấu hình scala khi khởi động
Arg

Heiko Seeberger SLF4S dường như không còn được duy trì. Tôi đã thực hiện nhắm mục tiêu của riêng mình Scala 2.10 và SLF4J mới nhất. http://slf4s.org/
Matt Roberts

3
Logula bị bỏ rơi theo README của nó trên Github.
Erik Kaplun

OnTHER Tạp chí có khái niệm tương tự như Scala Logging, nhưng các thông điệp nhật ký được gửi đến một diễn viên gọi SLF4J từ nhóm luồng chuyên dụng. Giao dịch thời gian CPU cho độ trễ (tốt hơn nhiều). github.com/onTHER/journal
Gary Coady

64

Với Scala 2.10+ Hãy xem xét ScalaLogging theo Formsafe. Sử dụng macro để cung cấp API rất sạch

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

Trích dẫn từ wiki của họ:

May mắn thay, các macro Scala có thể được sử dụng để làm cho cuộc sống của chúng ta dễ dàng hơn: ScalaLogging cung cấp lớp Loggervới các phương thức ghi nhật ký nhẹ sẽ được mở rộng thành thành ngữ trên. Vì vậy, tất cả chúng ta phải viết là:

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

Sau khi macro được áp dụng, mã sẽ được chuyển thành thành ngữ được mô tả ở trên.

Ngoài ra, ScalaLogging cung cấp đặc điểm Loggingcung cấp một Loggerthể hiện được khởi tạo một cách thuận tiện với tên của lớp được trộn vào:

import com.typesafe.scalalogging.slf4j.LazyLogging

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

12
Sử dụng class MyClass with Logging.
Andrzej Jozwik

1
Xin lưu ý rằng việc sử dụng đăng nhập vào các đặc điểm làm cho đặc điểm đó có cùng tên với logger như lớp được trộn vào. Tôi nghĩ rằng bạn có thể lấy logger bằng tay thay vì tiện lợi macro để cung cấp tên logger bằng tay, nếu cần. Nhưng làm ơn, hãy sửa tôi nếu tôi sai.
mkko

1
Điều này có nghĩa là MyClass có các phương thức công khai để gỡ lỗi, cảnh báo thông tin, v.v.? Nếu vậy, tôi thà làm công việc thủ công thêm là khởi tạo một logger riêng tư cho lớp. Các phương thức ghi nhật ký không được rò rỉ vào giao diện lớp.
ChucK

2
bạn nhận được một trường logger, mà bạn có thể sử dụng lúc rảnh rỗi. Xem github.com/typesafehub/scalalogging/blob/master/ cấp để biết chi tiết
fracca

2
2.x yêu cầu Scala 2.11; hy vọng không có nhiều sự khác biệt thực tế; cho Scala 2.10.x, có "com.typesafe" %% "scalalogging-slf4j" % "1.1.0".
Erik Kaplun

14

Sử dụng slf4j và trình bao bọc là tốt nhưng việc sử dụng phép nội suy được xây dựng bị phá vỡ khi bạn có nhiều hơn hai giá trị để nội suy, do đó bạn cần tạo một mảng các giá trị để nội suy.

Một giải pháp giống Scala hơn là sử dụng một thunk hoặc cluster để trì hoãn việc ghép thông báo lỗi. Một ví dụ điển hình của việc này là logger của thang máy

Log.scala Slf4jLog.scala

Trông như thế này:

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

Lưu ý rằng tin nhắn là một cuộc gọi theo tên và sẽ không được đánh giá trừ khi isTraceEnables là đúng vì vậy không có chi phí trong việc tạo ra một chuỗi tin nhắn đẹp. Điều này hoạt động xung quanh cơ chế nội suy của slf4j yêu cầu phân tích thông báo lỗi. Với mô hình này, bạn có thể nội suy bất kỳ số lượng giá trị nào vào thông báo lỗi.

Nếu bạn có một đặc điểm riêng biệt trộn Log4JLogger này vào lớp của bạn, thì bạn có thể làm

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

thay vì

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
có thể lấy số dòng chính xác để ghi nhật ký trong các lớp scala không ?????
jpswain

13

Đừng sử dụng Logula

Tôi thực sự đã làm theo khuyến nghị của Eugene và đã thử nó và phát hiện ra rằng nó có cấu hình vụng về và bị lỗi, không được sửa (chẳng hạn như cái này ). Nó không được duy trì tốt và nó không hỗ trợ Scala 2.10 .

Sử dụng slf4s + slf4j-đơn giản

Lợi ích chính:

  • Hỗ trợ Scala 2.10 mới nhất (đến nay là M7)
  • Cấu hình là linh hoạt nhưng không thể đơn giản hơn. Nó được thực hiện với các thuộc tính hệ thống , mà bạn có thể thiết lập bằng cách nối thêm một số thứ như -Dorg.slf4j.simplelogger.defaultlog=tracethực thi lệnh hoặc mã cứng trong tập lệnh của mình:System.setProperty("org.slf4j.simplelogger.defaultlog", "trace") . Không cần phải quản lý tập tin cấu hình rác!
  • Phù hợp độc đáo với IDE. Chẳng hạn, để đặt mức ghi nhật ký thành "theo dõi" trong cấu hình chạy cụ thể trong IDEA, chỉ cần truy cập Run/Debug Configurationsvà thêm -Dorg.slf4j.simplelogger.defaultlog=tracevào VM options.
  • Dễ dàng thiết lập: chỉ cần bỏ phần phụ thuộc từ cuối câu trả lời này

Đây là những gì bạn cần để chạy nó với 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>

Bạn tìm slf4 ở đâu hỗ trợ 2.10? Tôi đang xem github.com/weiglewilczek/slf4s và xem "Các phiên bản Scala được hỗ trợ là 2.8.0, 2.8.1, 2.9.0-1 và 2.9.1." .. tôi đang nhìn nhầm chỗ à?
nairbv

@Brian Sử dụng 2.9.1 như đề xuất trong câu trả lời. Tôi đã thử nghiệm nó hoạt động tốt với 2.10.0-M7
Nikita Volkov

Tôi đang sử dụng 2.9.1, tôi chỉ tò mò về "lợi ích chính" được tô sáng và ý nghĩa của nó.
nairbv

Sau khi tìm kiếm cẩn thận thông qua các câu trả lời cho những ưu và nhược điểm của các thư viện ghi nhật ký, tôi chọn một trong đó cho tôi biết phụ thuộc nào để nhập. Cảm ơn ngài.
teo

11

Đây là cách tôi có Scala Logging làm việc cho tôi:

Đặt cái này trong build.sbt:

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

Sau đó, sau khi thực hiện sbt update, điều này sẽ in ra một thông điệp nhật ký thân thiện:

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

Nếu bạn đang sử dụng Play, tất nhiên bạn có thể chỉ cần import play.api.Loggerviết thông điệp tường trình : Logger.debug("Hi").

Xem tài liệu để biết thêm.


Chỉ làm việc cho tôi sau khi tôi tạo log4j.propertiestrong thư mục tài nguyên dự án.
Nadjib Mami

7

Tôi đã tạo ra một chút công việc tạo thành Loggingđặc điểm scalaxvà tạo ra một đặc điểm cũng tích hợp mộtMessageFormat-based thư viện.

Sau đó, những thứ trông như thế này:

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

Chúng tôi thích cách tiếp cận cho đến nay.

Thực hiện:

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

Dễ thương, mặc dù tôi có một số vấn đề khiến nó hoạt động - cụ thể, đầu ra (trên cấu hình slf4j mặc định) luôn trông giống như sau, thay vì lớp thực sự mở rộng Loggable: Jul 22, 2011 3:02:17 AM redacted.util.Loggable $ thông tin lớp INFO: Một số tin nhắn ... Bất kỳ cơ hội nào bạn gặp phải điều này?
Tomer Gabel

6

Tôi sử dụng SLF4J + Logback classic và áp dụng nó như thế này:

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

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

Sau đó, bạn có thể sử dụng nó phù hợp với phong cách của bạn hơn:

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

nhưng cách tiếp cận này tất nhiên sử dụng một thể hiện logger cho mỗi thể hiện của lớp.


4

Bạn nên xem thư viện scalax: http://scalax.scalaforge.org/ Trong thư viện này, có một đặc điểm Ghi nhật ký, sử dụng sl4j làm phụ trợ. Bằng cách sử dụng đặc điểm này, bạn có thể đăng nhập khá dễ dàng (chỉ cần sử dụng trường logger trong lớp kế thừa tính trạng).


4

Writer, MonoidMonadthực hiện.


5
Bạn có thể giải thích những gì bạn có ý nghĩa?
David J.

Kết hợp những thứ đó có thể tạo ra một "nhật ký" đẹp kèm theo kết quả của bạn. IMO, nó là một chút quá mức cần thiết. Nhưng nếu bạn muốn tìm hiểu một cái gì đó mới blog.tmorris.net/the-writer-monad-USE-scala-example
Tg.

@Tg. Các liên kết trong bình luận của bạn là chết. Sử dụng blog.tmorris.net/posts/the-writer-monad-USE-scala-example
jub0bs


3

Hình thức nhanh chóng và dễ dàng.

Scala 2.10 trở lên:

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

Và build.sbt:

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

Scala 2.11+ và mới hơn:

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

Và build.sbt:

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

2

Sau khi sử dụng slf4s và logula một lúc, tôi đã viết loglady, một đặc điểm đăng nhập đơn giản bao gồm slf4j.

Nó cung cấp một API tương tự như thư viện ghi nhật ký của Python, làm cho các trường hợp phổ biến (chuỗi cơ bản, định dạng đơn giản) trở nên tầm thường và tránh định dạng soạn sẵn.

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


2

Tôi thấy rất thuận tiện khi sử dụng một số loại java logger, ví dụ sl4j, với trình bao bọc scala đơn giản, mang lại cho tôi cú pháp như vậy

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

object User with dsl.logging {

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

}

Theo ý kiến ​​của tôi, mixin rất hữu ích của các khung ghi nhật ký đã được chứng minh của java và cú pháp ưa thích của scala.


@sschaef, ồ, vâng. Lấy làm tiếc. Tôi gần như đã quên. Đó là một cuộc chiến tuyệt vời với vấn đề này trong vài ngày, nhưng có vẻ như điều này thực sự là không thể. Tôi đã sử dụng #! ! "Thông tin thông tin" thay vào đó. Tôi sẽ chỉnh sửa chỉnh sửa câu trả lời của tôi.
Alex Povar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.