2010-01-07 28 views
28

Trong Java, thành ngữ chuẩn để ghi nhật ký là tạo một biến tĩnh cho một đối tượng logger và sử dụng nó trong các phương thức khác nhau.đăng nhập scala

Trong Scala, có vẻ như thành ngữ là tạo ra đặc điểm Ghi nhật ký với một thành viên logger và trộn lẫn đặc điểm trong các lớp cụ thể. Điều này có nghĩa rằng mỗi khi một đối tượng được tạo ra nó gọi khung đăng nhập để có được một logger và cũng là đối tượng lớn hơn do tham chiếu bổ sung.

Có cách nào khác cho phép dễ sử dụng "có ghi nhật ký" trong khi vẫn sử dụng cá thể trình ghi nhật ký cho mỗi lớp không? EDIT: Câu hỏi của tôi không phải là về cách người ta có thể viết một khung ghi nhật ký trong Scala, mà là cách sử dụng một khung công tác hiện có (log4j) mà không phải chịu chi phí cho hiệu suất (nhận tham chiếu cho từng cá thể) hoặc mã phức tạp. Ngoài ra, có, tôi muốn sử dụng log4j, đơn giản bởi vì tôi sẽ sử dụng thư viện của bên thứ ba được viết bằng Java có khả năng sử dụng log4j.

+0

đường dẫn đăng nhập là gì? – Adrian

Trả lời

19

Tôi chỉ gắn bó với phương pháp "với Ghi nhật ký". Thiết kế sạch sẽ chiến thắng mọi lúc - nếu bạn có được bản mẫu hóa ra thì có khả năng bạn có thể tìm thấy những lợi ích hữu ích hơn rất nhiều có thể đạt được ở các khu vực khác. Hãy nhớ rằng khung đăng nhập sẽ lưu vào bộ nhớ cache, vì vậy bạn vẫn có một lớp cho mỗi lớp, ngay cả khi mọi cá thể của lớp đó xảy ra để giữ một tham chiếu (không tốn kém).

Nếu không có bằng chứng cho thấy tài liệu tham khảo logger đang làm hại heap của bạn, điều này có mùi giống như tối ưu hóa sớm ... Chỉ cần thư giãn và không lo lắng về nó, trừ khi một profiler nói với bạn nếu không.

Trên ghi chú không liên quan, bạn cũng có thể muốn xem xét sử dụng slf4j và logback thay vì log4j. slf4j có thiết kế sạch hơn phù hợp hơn với scala thành ngữ.

+0

sau đó nó trông với tôi đây là một trường hợp mà Java thắng trên Scala (đặc biệt là nếu bạn có hỗ trợ IDE, nơi mã giải mã biến log tĩnh có thể được tạo ra bởi IDE). Scala vẫn còn ở phía trước trong cuộc đua, nhưng tôi đã hy vọng trường hợp sử dụng này sẽ được giải quyết bởi một ai đó. Và điều này thực sự là tối ưu hóa sớm, nhưng nó chạm vào từng bit mã của hệ thống của tôi. Tôi không thấy cách tôi có thể tối ưu hóa sau này. – IttayD

+1

Tôi đã thực sự nhìn thấy phương pháp này mỗi trường hợp được sử dụng trong Java, tôi thậm chí đã sử dụng nó bản thân mình. Lợi ích chính là bạn có thể chỉ định 'Logger.getLogger (this.class)' khi xây dựng trình ghi nhật ký - điều này tránh được các vấn đề khi loại mã mở đầu này được sao chép giữa các định nghĩa lớp –

+3

Tôi có đặc điểm này 'trait J2SELogging {protected val log = Logger.getLogger (getClass.getName)} ' – fommil

3
object Log { 
    def log(message: String) = { 
     ..... 
    } 
} 

Không?

+0

Tôi nghĩ rằng đây là giải pháp tốt nhất, bởi vì tôi không nghĩ rằng nó là thông minh để mở rộng/thực hiện một đặc điểm khi không có "là một" mối quan hệ. – paradigmatic

+1

Đây là lớp sử dụng hỗ trợ ghi nhật ký. :-) Danh mục lớp/đăng nhập bị thiếu. Nó phải có ít nhất là 'Log.log (c: Class [_], message: String) ' –

1

Dưới đây là một hack nhanh chóng (mà tôi chưa thực sự được sử dụng, trung thực; @)

object LogLevel extends Enumeration { 
    val Error = Value(" ERROR ") 
    val Warning = Value(" WARNING ")                          
    val Info = Value(" INFO ") 
    val Debug = Value(" DEBUG ") 
} 

trait Identity { 
    val id: String 
} 

trait Logging extends Identity { 
    import LogLevel._ 

    abstract class LogWriter { 
    protected val writer: Actor 
    protected val tstampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ") 

    def tstamp = tstampFormat.format(new Date) 

    def log(id: String, logLevel: LogLevel.Value, msg: String) { 
     writer ! (tstamp + id + logLevel + msg) 
    } 
    } 

    object NullLogWriter extends LogWriter { 
    protected val writer = actor{loop {react{case msg: String =>}}} 
    } 

    object ConsoleWriter extends LogWriter { 
    protected val writer = actor{loop {react {case msg: String => Console.out.println(msg); Console.flush case _ =>}}} 
    } 

    class FileWriter(val file: File) extends LogWriter { 
    require(file != null) 
    require(file.canWrite) 

    protected val writer = actor{loop {react {case msg: String => destFile.println(msg); destFile.flush case _ =>}}} 

    private val destFile = { 
     try {new PrintStream(new FileOutputStream(file))} 
     catch {case e => ConsoleWriter.log("FileWriter", LogLevel.Error, "Unable to create FileWriter for file " + file + 
             " exception was: " + e); Console.out} 
    } 
    } 

    protected var logWriter: LogWriter = ConsoleWriter 
    protected var logLevel    = Info 

    def setLoggingLevel(level: LogLevel.Value) {logLevel = level} 

    def setLogWriter(lw: LogWriter) {if (lw != null) logWriter = lw} 

    def logError(msg: => String) {if (logLevel <= Error) logWriter.log(id, Error, msg)} 

    def logWarning(msg: => String) {if (logLevel <= Warning) logWriter.log(id, Warning, msg)} 

    def logInfo(msg: => String) {if (logLevel <= Info) logWriter.log(id, Info, msg)} 

    def logDebug(msg: => String) {if (logLevel <= Debug) logWriter.log(id, Debug, msg)} 
} 

Hy vọng đó là của một số sử dụng.

+0

cách này trả lời câu hỏi của tôi? tôi đã không hỏi làm thế nào bạn có thể viết một khung khai thác gỗ trong scala. tôi hỏi làm thế nào bạn có thể sử dụng một hiện tại (nói log4j) – IttayD

0

Một cách là mở rộng Logger đến đối tượng đồng:

object A extends LoggerSupport 

class A { 
    import A._ 
    log("hi") 
} 

trait LoggerSupport{ 
    val logger = LoggerFactory.getLogger(this.getClass) 
    def log(msg : String)= logger.log(msg) 
} 

//classes of the logging framework 
trait Logger{ 
    def log(msg : String) : Unit 
} 

object LoggerFactory{ 
    def getLogger(classOfLogger : Class[_]) : Logger = ... 
} 

Hoặc bạn có thể bộ nhớ cache trường logger:

import collection.mutable.Map 
object LoggerCache{ 
    var cache : Map[Class[_], Logger] = Map() 
    def logger(c : Class[_]) = cache.getOrElseUpdate(c, LoggerFactory.getLogger(c)) 
} 

trait LoggerSupport{ 
    def log(msg : String) = LoggerCache.logger(this.getClass).log(msg) 
} 

class A extends LoggerSupport{ 
    log("hi") 
} 

Đây là dễ dàng hơn để sử dụng nhưng sẽ có hiệu suất tồi tệ hơn. Hiệu năng sẽ rất tệ khi bạn loại bỏ hầu hết các thông điệp tường trình (vì cấu hình mức nhật ký).

+0

có, nhưng sau đó nó là như cồng kềnh như java tương đương ... – IttayD

+0

cũng lưu ý rằng trình ghi nhật ký sẽ được phân loại theo lớp của đối tượng mà trong JVM nằm bên dưới không phải là tên lớp giống với lớp thực tế (một '$' ở cuối) gây nhầm lẫn (ai đó muốn thiết lập mức gỡ lỗi và phải nhớ đặt nó cho lớp của đối tượng đồng hành) – IttayD

+0

Có, phiên bản đối tượng không đẹp. Tôi đã thêm một giải pháp thay thế để lưu trữ các cá thể Logger. Nhưng nó vẫn không hoàn hảo. –

11

Tôi đã sử dụng log4j với Scala bằng cách tạo một đặc điểm và có trình ghi nhật ký theo từng trường hợp không phải mỗi lớp. Với một số phép thuật và biểu hiện Scala, bạn có thể thay đổi logger thành tĩnh (đối tượng bên trong), nhưng tôi không chắc chắn 100%. Cá nhân, tôi đồng ý với @KevinWright làm cho trình ghi nhật ký tĩnh là một tối ưu hóa sớm. Cũng cần lưu ý rằng mã bên dưới có các thông điệp tường trình như tên, có nghĩa là các cuộc gọi nhật ký của bạn không cần phải được bao bọc trong `if (log.isDebugEnabled()); các thông điệp tường trình phức tạp được tạo thông qua nối chuỗi sẽ không được đánh giá trừ khi mức nhật ký là thích hợp.Xem liên kết này để biết thêm: http://www.naildrivin5.com/scalatour/wiki_pages/TypeDependentClosures

http://github.com/davetron5000/shorty/blob/master/src/main/scala/shorty/Logs.scala

package shorty 

import org.apache.log4j._ 

trait Logs { 
    private[this] val logger = Logger.getLogger(getClass().getName()); 

    import org.apache.log4j.Level._ 

    def debug(message: => String) = if (logger.isEnabledFor(DEBUG)) logger.debug(message) 
    def debug(message: => String, ex:Throwable) = if (logger.isEnabledFor(DEBUG)) logger.debug(message,ex) 
    def debugValue[T](valueName: String, value: => T):T = { 
    val result:T = value 
    debug(valueName + " == " + result.toString) 
    result 
    } 

    def info(message: => String) = if (logger.isEnabledFor(INFO)) logger.info(message) 
    def info(message: => String, ex:Throwable) = if (logger.isEnabledFor(INFO)) logger.info(message,ex) 

    def warn(message: => String) = if (logger.isEnabledFor(WARN)) logger.warn(message) 
    def warn(message: => String, ex:Throwable) = if (logger.isEnabledFor(WARN)) logger.warn(message,ex) 

    def error(ex:Throwable) = if (logger.isEnabledFor(ERROR)) logger.error(ex.toString,ex) 
    def error(message: => String) = if (logger.isEnabledFor(ERROR)) logger.error(message) 
    def error(message: => String, ex:Throwable) = if (logger.isEnabledFor(ERROR)) logger.error(message,ex) 

    def fatal(ex:Throwable) = if (logger.isEnabledFor(FATAL)) logger.fatal(ex.toString,ex) 
    def fatal(message: => String) = if (logger.isEnabledFor(FATAL)) logger.fatal(message) 
    def fatal(message: => String, ex:Throwable) = if (logger.isEnabledFor(FATAL)) logger.fatal(message,ex) 
} 

class Foo extends SomeBaseClass with Logs { 
    def doit(s:Option[String]) = { 
    debug("Got param " + s) 
    s match { 
     case Some(string) => info(string) 
     case None => error("Expected something!") 
    } 
    } 
} 
+0

Tôi đã thử mã của bạn trong nhật thực ... nhưng thông báo gỡ lỗi đã không in trên bảng điều khiển..do tôi cần bất kỳ điều gì khác, vui lòng hướng dẫn – HDev007

5

Nếu bạn đang thực sự quan tâm đến không gian trên cao và/hoặc thêm thời gian trong initializers đối tượng, một chiến lược tốt có thể có một đặc điểm khai thác gỗ mà rời logger trừu tượng như trong

 

trait Logging { 
    def logger: Logger 
    def debug(message: String) { logger.debug(message) } 
    def warn(message: String) { logger.warn(message) } 
} 
 

Đối với các lớp học mà cần phải được làm nhẹ càng tốt thì bạn có thể làm

 

object MustBeLightweight { 
    val logger = Logger.getLog(classOf[MustBeLightweight]) 
} 
class MustBeLightWeight extends Logging { 
    final def logger = MustBeLightweight.logger 
} 
 

JIT thậm chí có thể in debugwarnlogger trong trường hợp này.

Bạn cũng có thể có một đặc điểm để trộn ở cho các lớp học nơi những phí của một trường bổ sung không phải là một vấn đề


trait PerInstanceLog { 
    val logger = Logger.getLog(this.getClass()) 
} 

Một lựa chọn nữa là để lại đăng xuất khỏi lớp và đặt nó hoàn toàn trong một đối tượng như trong


object Foo { 
    object log extends Logging { 
    override val logger = Logger.getLogger(classOf[Foo]) 
    } 
} 

class Foo { 
    import Foo.log._ 
    def someMethod() = { warn("xyz") } 
} 

Tôi đồng ý với Kevin, đừng thêm sự phức tạp trừ khi bạn cần.

2

Đôi khi đăng nhập ở cấp gói là câu trả lời đúng. Scala làm cho điều này dễ hơn java vì các đối tượng có thể được định nghĩa trực tiếp trong một gói. Nếu bạn đã xác định Nhật ký như sau:

package example 
object Log extends au.com.langdale.util.PackageLogger 

Nhật ký này khả dụng ở mọi nơi trong ví dụ gói. Để có được ghi nhật ký chi tiết hơn, bạn có thể phân tán các định nghĩa tương tự xung quanh cấu trúc phân cấp gói. Hoặc bạn có thể định nghĩa tất cả các logger gói với nhau như thế này:

package example { 
    import au.com.langdale.util.PackageLogger 

    object Log extends PackageLogger 

    package frobber { 
    object Log extends PackageLogger 

    package undulater { 
     object Log extends PackageLogger 
    } 
    } 
} 

Lớp PackageLogger có thể được định nghĩa như sau (giả sử SLF4J):

package au.com.langdale.util 
import org.slf4j.LoggerFactory 

class PackageLogger { 
    val name = { val c = getClass.getName; c.substring(0, c.lastIndexOf('.')) } 
    val inner = LoggerFactory.getLogger(name) 

    // various convenient logging methods follow.... 
    def apply(mesg: => Any) = inner.info(mesg.toString) 
    def info(mesg: String) = inner.info(mesg) 
    def warn(mesg: String) = inner.warn(mesg) 
    def error(mesg: String) = inner.error(mesg) 
} 
1

API typesafe khai thác gỗ (https://github.com/typesafehub/scalalogging) có một đặc điểm cho thêm một logger val vào một class nhưng việc sử dụng nó là tùy chọn. Nó khởi tạo biến bằng cách sử dụng getClass getName mà một nửa thời gian sẽ là vô giá trị bởi vì thường xuyên tên lớp thực tế của bạn sẽ là gobbledygook. Vì vậy, nếu bạn không muốn đặc điểm thêm biến phụ vào mỗi cá thể bạn chắc chắn không cần phải sử dụng nó và có thể chỉ cần đặt giá trị logger trong đối tượng đồng hành của bạn và thực hiện nhập trong lớp của bạn đồng hành các thành viên đối tượng, do đó bạn không cần phải đủ điều kiện.