เข้าสู่ระบบใน Scala


168

วิธีที่ดีในการเข้าสู่ระบบในแอปพลิเคชัน Scala คืออะไร? สิ่งที่สอดคล้องกับปรัชญาภาษาไม่เกะกะรหัสและบำรุงรักษาต่ำและไม่เป็นการรบกวน นี่คือรายการข้อกำหนดพื้นฐาน:

  • ง่าย
  • ไม่ถ่วงรหัส สกาล่านั้นยอดเยี่ยมสำหรับความกะทัดรัด ฉันไม่ต้องการให้รหัสครึ่งหนึ่งของฉันเป็นบันทึกการทำงาน
  • รูปแบบการบันทึกสามารถเปลี่ยนแปลงได้เพื่อให้พอดีกับส่วนที่เหลือของบันทึกองค์กรของฉันและซอฟต์แวร์การตรวจสอบ
  • รองรับระดับการบันทึก (เช่น debug, trace, error)
  • สามารถเข้าสู่ดิสก์เช่นเดียวกับปลายทางอื่น ๆ (เช่นซ็อกเก็ตคอนโซล ฯลฯ )
  • การกำหนดค่าขั้นต่ำถ้ามี
  • ทำงานในภาชนะ (เช่นเว็บเซิร์ฟเวอร์)
  • (เป็นทางเลือก แต่ก็ดีมี) มาเป็นส่วนหนึ่งของภาษาหรือเป็นสิ่งประดิษฐ์ maven ดังนั้นฉันไม่ต้องแฮ็คงานสร้างของฉันเพื่อใช้งาน

ฉันรู้ว่าฉันสามารถใช้โซลูชันการบันทึก Java ที่มีอยู่ได้ แต่พวกเขาล้มเหลวอย่างน้อยสองข้อข้างต้นนั่นคือความยุ่งเหยิงและการกำหนดค่า

ขอบคุณสำหรับคำตอบของคุณ

คำตอบ:


119

ห่อ slf4j

ไลบรารีการบันทึกของ Scala ส่วนใหญ่มีการล้อมรอบเฟรมเวิร์กการบันทึกข้อมูล Java (slf4j, log4j และอื่น ๆ ) แต่เมื่อเดือนมีนาคม 2558 ไลบรารีบันทึกการรอดชีวิตทั้งหมดคือ slf4j ห้องสมุดบันทึกเหล่านี้มีการจัดเรียงของบางlogวัตถุที่คุณสามารถโทรinfo(...), debug(...)ฯลฯ ฉันไม่ได้เป็นแฟนตัวยงของ slf4j แต่ตอนนี้ดูเหมือนว่าจะเป็นกรอบการเข้าสู่ระบบที่โดดเด่น นี่คือคำอธิบายของSLF4J :

Simple Logging Facade สำหรับ Java หรือ (SLF4J) ทำหน้าที่เป็นซุ้มอย่างง่ายหรือนามธรรมสำหรับกรอบการบันทึกต่างๆเช่น java.util.logging, log4j และ logback ทำให้ผู้ใช้สามารถเสียบกรอบการบันทึกที่ต้องการในเวลาการปรับใช้

ความสามารถในการเปลี่ยนไลบรารีบันทึกพื้นฐาน ณ เวลาที่ใช้งานจะทำให้เกิดคุณลักษณะที่เป็นเอกลักษณ์ของกลุ่มตัดไม้ตระกูล slf4j ทั้งหมดซึ่งคุณต้องระวัง:

  1. classpath เป็นวิธีการกำหนดค่า วิธีที่ slf4j รู้ว่าไลบรารีการบันทึกพื้นฐานที่คุณใช้คือการโหลดคลาสด้วยชื่อบางอย่าง ฉันมีปัญหาในการที่ slf4j ไม่รู้จักตัวบันทึกของฉันเมื่อ classloader ถูกกำหนดเอง
  2. เนื่องจากfacade แบบธรรมดาพยายามเป็นตัวส่วนร่วมจึงถูก จำกัด เฉพาะการเรียกใช้บันทึกจริงเท่านั้น กล่าวอีกนัยหนึ่งการกำหนดค่าไม่สามารถทำได้ผ่านรหัส

ในโครงการขนาดใหญ่จริง ๆ แล้วมันจะสะดวกจริง ๆ ที่จะสามารถควบคุมพฤติกรรมการบันทึกของการพึ่งพาสกรรมกริยาถ้าทุกคนใช้ slf4j

การบันทึกสกาล่า

Scala เข้าสู่ระบบที่เขียนโดย Heiko Seeberger เป็นทายาทที่เขาslf4s มันใช้แมโครเพื่อขยายการโทรเข้าถ้านิพจน์เพื่อหลีกเลี่ยงการโทรที่อาจมีราคาแพง

Scala Logging เป็นไลบรารี่การบันทึกไลบรารี่ที่สะดวกและมีประสิทธิภาพเช่น SLF4J และอื่น ๆ

ตัวบันทึกประวัติ

  • Logulaเสื้อคลุม Log4J เขียนโดย Coda Hale เคยชอบสิ่งนี้ แต่ตอนนี้มันถูกทิ้งร้าง
  • configgy , ตัวห่อหุ้ม java.util.logging ที่เคยเป็นที่นิยมในสมัยก่อนของ Scala ตอนนี้ถูกทอดทิ้ง

1
ต้องบอกว่าฉันรัก Logula ... ในที่สุดคนตัดไม้ที่ไม่ทำให้ฉันต้องการที่จะแทงหูของฉัน ไม่มี XML และไม่มี BS เพียงการตั้งค่าสกาล่าในการเริ่มต้น
Arg

SLF4S ของ Heiko Seeberger ดูเหมือนจะไม่ได้รับการบำรุงรักษาอีกต่อไป ฉันตั้งเป้าหมายสกาล่า 2.10 และ SLF4J ล่าสุด http://slf4s.org/
Matt Roberts

3
Logula ถูกทอดทิ้งตาม README ใน Github
Erik Kaplun

Oncue Journal นั้นคล้ายคลึงกับแนวคิดของ Scala Logging แต่ข้อความบันทึกถูกส่งไปยังนักแสดงที่เรียก SLF4J จากเธรดพูลเฉพาะ ทำการค้าเวลา CPU สำหรับเวลาแฝง (ดีกว่า)github.com/oncue/journal
Gary Coady

64

ด้วย Scala 2.10+ พิจารณา ScalaLogging โดย Typesafe ใช้มาโครเพื่อส่ง API ที่สะอาดมาก

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

ข้อความจากวิกิ:

โชคดีที่สามารถใช้มาโคร Scala เพื่อทำให้ชีวิตของเราง่ายขึ้น: ScalaLogging เสนอคลาสLoggerด้วยวิธีการบันทึกที่มีน้ำหนักเบาซึ่งจะขยายไปสู่สำนวนข้างต้น ดังนั้นสิ่งที่เราต้องเขียนคือ:

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

หลังจากที่มีการใช้งานแมโครรหัสจะถูกแปลงเป็นสำนวนที่อธิบายไว้ข้างต้น

นอกจากนี้ ScalaLogging ยังมีคุณสมบัติLoggingที่ให้Loggerตัวอย่างที่เริ่มต้นได้อย่างสะดวกด้วยชื่อของคลาสที่ผสมกันเป็น:

import com.typesafe.scalalogging.slf4j.LazyLogging

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

12
class MyClass with Loggingใช้
Andrzej Jozwik

1
โปรดทราบว่าการใช้การเข้าสู่ระบบในลักษณะที่ทำให้เกิดลักษณะที่จะมีชื่อเดียวกันสำหรับคนตัดไม้เป็นคลาสที่มันถูกผสมลงใน ฉันคิดว่าคุณสามารถรับ logger ด้วยตนเองแทนความสะดวกสบายของแมโครเพื่อให้ชื่อ logger ด้วยตนเองถ้าจำเป็น แต่โปรดแก้ไขให้ฉันถ้าฉันผิด
mkko

1
สิ่งนี้หมายความว่า MyClass มีวิธีสาธารณะสำหรับการดีบัก, ข้อมูลเตือน ฯลฯ ? ถ้าเป็นเช่นนั้นฉันควรจะทำงานพิเศษด้วยตนเองในการสร้างคนตัดไม้ให้เป็นส่วนตัวในชั้นเรียน วิธีการบันทึกไม่ควรรั่วไหลไปสู่อินเตอร์เฟสของคลาส
ChucK

2
คุณได้รับฟิลด์ตัวบันทึกซึ่งคุณสามารถใช้ในยามว่างได้ ดูgithub.com/typesafehub/scalalogging/blob/master/…สำหรับรายละเอียด
fracca

2
2.x ต้องการ Scala 2.11; หวังว่าจะมีความแตกต่างในทางปฏิบัติไม่มาก สำหรับ Scala 2.10.x "com.typesafe" %% "scalalogging-slf4j" % "1.1.0"มี
Erik Kaplun

14

การใช้ slf4j และ wrapper นั้นดี แต่การใช้มันสร้างขึ้นในการแก้ไขแบ่งย่อยเมื่อคุณมีค่ามากกว่าสองค่าในการแก้ไขตั้งแต่นั้นคุณต้องสร้าง Array of values ​​เพื่อ interpolate

วิธีแก้ปัญหาแบบสกาล่าที่มากขึ้นคือการใช้ thunk หรือ cluster เพื่อชะลอการต่อข้อความที่ผิดพลาด ตัวอย่างที่ดีของสิ่งนี้คือตัวบันทึกของลิฟต์

Log.scala Slf4jLog.scala

ซึ่งมีลักษณะเช่นนี้:

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

โปรดทราบว่า msg เป็นชื่อการโทรและจะไม่ถูกประเมินเว้นแต่ isTraceEnabled เป็นจริงดังนั้นจึงไม่มีค่าใช้จ่ายในการสร้างสตริงข้อความที่ดี วิธีนี้ใช้ได้กับกลไกการแก้ไขของ 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
เป็นไปได้หรือไม่ที่จะได้รับหมายเลขบรรทัดที่ถูกต้องสำหรับการบันทึกคำสั่งในคลาส scala ?????
jpswain

13

อย่าใช้ Logula

ฉันทำตามคำแนะนำของยูจีนจริง ๆ แล้วลองใช้และพบว่ามันมีโครงร่างเงอะงะและอยู่ภายใต้ข้อบกพร่องซึ่งไม่ได้รับการแก้ไข (เช่นอันนี้ ) ดูเหมือนจะไม่ได้รับการดูแลอย่างดีและไม่รองรับ Scala 2.102.10

ใช้ slf4s + slf4j-simple

ประโยชน์ที่สำคัญ:

  • รองรับ Scala 2.10 ล่าสุด (จนถึงปัจจุบัน M7)
  • การกำหนดค่าใช้งานได้หลากหลาย แต่ไม่ง่ายกว่านี้ มันทำกับคุณสมบัติของระบบซึ่งคุณสามารถตั้งค่าโดยการผนวกสิ่งที่ต้องการ-Dorg.slf4j.simplelogger.defaultlog=traceคำสั่งการดำเนินการหรือ hardcode ในสคริปต์ของคุณ:System.setProperty("org.slf4j.simplelogger.defaultlog", "trace")กับคำสั่งการดำเนินการหรือฝังในสคริปต์ของคุณ:ไม่จำเป็นต้องจัดการไฟล์กำหนดค่าไร้ค่า!
  • เหมาะอย่างยิ่งกับ IDEs ยกตัวอย่างเช่นการตั้งค่าระดับการเข้าสู่ระบบที่จะ "ร่องรอย" ในการกำหนดค่าเฉพาะทำงานในความคิดเพียงแค่ไป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>

คุณจะหา slf4s ที่รองรับ 2.10 ได้ที่ไหน? ฉันกำลังดูgithub.com/weiglewilczek/slf4sแล้วดู "รุ่น Scala ที่รองรับคือ 2.8.0, 2.8.1, 2.9.0-1 และ 2.9.1" .. ฉันกำลังมองหาที่ผิดหรือเปล่า?
nairbv

@Brian ใช้ 2.9.1 ตามที่แนะนำในคำตอบ ฉันทดสอบว่าใช้งานได้ดีกับ 2.10.0-M7
Nikita Volkov

ฉันใช้ 2.9.1 ฉันแค่อยากรู้เกี่ยวกับ "ผลประโยชน์หลัก" ที่ไฮไลต์และสิ่งที่หมายถึง
nairbv

หลังจากค้นหาคำตอบสำหรับข้อดีข้อเสียของไลบรารีการบันทึกอย่างถี่ถ้วนแล้วฉันเลือกสิ่งที่บอกฉันว่าต้องพึ่งพาการนำเข้าอย่างไร ขอบคุณครับ
Teo

11

นี่คือวิธีที่ฉันได้รับการบันทึกสกาล่าให้ฉัน:

ใส่สิ่งนี้ใน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: 22 ก.ค. 2554 3:02:17 น. redacted.util.Loggable ข้อมูลระดับ $ 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 เป็นแบ็กเอนด์ โดยใช้ลักษณะนี้คุณสามารถเข้าสู่ระบบได้อย่างง่ายดาย (เพียงแค่ใช้ฟิลด์คนตัดไม้ในชั้นเรียนสืบทอดลักษณะ)


4

Writer, MonoidและMonadการดำเนินงาน


5
คุณสามารถอธิบายสิ่งที่คุณหมายถึงอะไร
David J.

การรวมกันของเหล่านั้นสามารถสร้าง "บันทึก" ที่ดีแนบไปกับผลลัพธ์ของคุณ IMO มันเกินความจริงเล็กน้อย แต่ถ้าคุณต้องการเรียนรู้สิ่งใหม่blog.tmorris.net/the-writer-monad-using-scala-example
Tg

@Tg ลิงก์ในความคิดเห็นของคุณตายไปแล้ว ใช้blog.tmorris.net/posts/the-writer-monad-using-scala-example
jub0bs


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 อยู่พักนึงผมก็เขียนว่าlogladyslf4j เป็นลักษณะการตัดคำแบบเรียบง่าย

มันมี API คล้ายกับไลบรารีการบันทึกของ Python ซึ่งทำให้กรณีทั่วไป (สตริงพื้นฐานการจัดรูปแบบง่าย ๆ ) เล็กน้อยและหลีกเลี่ยงการจัดรูปแบบสำเร็จรูป

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


2

ฉันพบว่าสะดวกมากโดยใช้ java logger บางชนิดเช่น sl4j กับ wrapper แบบสกาล่าอย่างง่ายซึ่งทำให้ฉันมีไวยากรณ์ดังกล่าว

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

object User with dsl.logging {

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

}

ในความคิดของฉันมีประโยชน์มาก mixin ของ java กรอบการบันทึกการพิสูจน์และไวยากรณ์แฟนซีของสกาล่า


@sschaef โอ้ใช่ ขอโทษ ฉันเกือบจะลืม มันเป็นการต่อสู้ที่ยอดเยี่ยมสำหรับปัญหานี้สองสามวัน แต่ดูเหมือนว่ามันเป็นไปไม่ได้จริงๆ ฉันใช้ #! ! "ข้อความข้อมูล" แทน ฉันจะแก้ไขคำตอบของฉัน
Alex Povar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.