All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fm.common.Logging.scala Maven / Gradle / Ivy

/*
 * Copyright 2014 Frugal Mechanic (http://frugalmechanic.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fm.common

import java.io.{File, OutputStream, PrintStream}
import ch.qos.logback.classic.{Level => LogbackLevel, Logger => LogbackLogger}
import org.apache.commons.io.output.TeeOutputStream
import org.slf4j.Logger.ROOT_LOGGER_NAME

/**
 * This can be extended for basic logging functionality
 */
trait Logging {
  @transient protected lazy val logger: Logger = Logger.getLogger(this)
}

// TODO: move this?
final case class LoggingCaptureConfig(logger: org.slf4j.Logger, pattern: String, file: File, overwrite: Boolean)

/**
* This has SLF4J/Logback Helpers that depend on SLF4j/Logback
*
* TODO: Clean this up!!
*/
object Logging {

 // Some helpers to set the logging level at runtime
 def setLevelToTrace(logger: AnyRef): Unit = setLevel(logger, LogbackLevel.TRACE)
 def setLevelToDebug(logger: AnyRef): Unit = setLevel(logger, LogbackLevel.DEBUG)
 def setLevelToInfo(logger: AnyRef):  Unit = setLevel(logger, LogbackLevel.INFO)
 def setLevelToWarn(logger: AnyRef):  Unit = setLevel(logger, LogbackLevel.WARN)
 def setLevelToError(logger: AnyRef): Unit = setLevel(logger, LogbackLevel.ERROR)
 def setLevelToOff(logger: AnyRef): Unit = setLevel(logger, LogbackLevel.OFF)

 // Set the ROOT logger
 def setRootLevelToTrace(): Unit = setRootLevel(LogbackLevel.TRACE)
 def setRootLevelToDebug(): Unit = setRootLevel(LogbackLevel.DEBUG)
 def setRootLevelToInfo():  Unit = setRootLevel(LogbackLevel.INFO)
 def setRootLevelToWarn():  Unit = setRootLevel(LogbackLevel.WARN)
 def setRootLevelToError(): Unit = setRootLevel(LogbackLevel.ERROR)
 def setRootLevelToOff(): Unit = setRootLevel(LogbackLevel.OFF)

 // Set the ROOT logger
 def setRootLevel(level: LogbackLevel): Unit = setLevel(ROOT_LOGGER_NAME, level)

 // Private to avoid exposing LogbackLevel which causes ProGuard issues
 private def setLevel(logger: AnyRef, level: LogbackLevel): Unit = setLevel(Logger.SLF4JLogger(logger).underlying, level)

 // Private to avoid exposing org.slf4j.Logger which causes ProGuard issues
 private def setLevel(logger: org.slf4j.Logger, level: LogbackLevel): Unit = logger match { case logback: LogbackLogger => logback.setLevel(level) }

 /**
  * Capture logging to a file
  *
  * Note: Private to avoid exposing org.slf4j.Logger which causes ProGuard issues
  */
 private def capture[T](
   logger: org.slf4j.Logger,
   pattern: String, // = """%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n""",
   file: File,
   overwrite: Boolean
 )(fun: => T): T = {
   FileUtil.writeFile(file, overwrite){ os => capture(logger.asInstanceOf[ch.qos.logback.classic.Logger], pattern, os)(fun) }
 }

 /**
  * Capture logging based on a LoggingCaptureConfig
  * Note: Private to avoid exposing org.slf4j.Logger (via LoggingCaptureConfig) which causes ProGuard issues
  */
 private def capture[T](configs: LoggingCaptureConfig*)(fun: => T): T = {
   if (configs.isEmpty) return fun

   val head: LoggingCaptureConfig = configs.head
   capture(head.logger, head.pattern, head.file, head.overwrite) {
     capture(configs.tail:_*)(fun)
   }
 }


 /**
  * Capture logging to an output stream
  *
  * Note: Private to avoid exposing ch.qos.logback.classic.Logger which causes ProGuard issues
  */
 private def capture[T](logger: ch.qos.logback.classic.Logger, pattern: String, os: OutputStream)(fun: => T): T = {
   import ch.qos.logback.classic.LoggerContext

   val ctx = org.slf4j.LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]
   val encoder = new ch.qos.logback.classic.encoder.PatternLayoutEncoder
   encoder.setContext(ctx)
   encoder.setPattern(pattern)
   encoder.start

   val osAppender = new ch.qos.logback.core.OutputStreamAppender[ch.qos.logback.classic.spi.ILoggingEvent]
   osAppender.setContext(ctx)
   osAppender.setLayout(encoder.getLayout)
   osAppender.setEncoder(encoder)
   osAppender.setOutputStream(os)
   osAppender.start()

   try {
     withAppender(logger.asInstanceOf[ch.qos.logback.classic.Logger], osAppender)(fun)
   } finally {
     osAppender.stop()
     encoder.stop()
   }
 }

 /**
  * Capture Standard Out and Standard Error to a file
  */
 def captureStdOutStdErr[T](file: File, overwrite: Boolean, append: Boolean = false, useTmpFile: Boolean = true)(fun: => T): T = {
   FileOutputStreamResource(file, overwrite = overwrite, append = append, useTmpFile = useTmpFile).use{ os => captureStdOutStdErr(os)(fun) }
 }

 /**
  * Capture Standard Out and Standard Error to an Output Stream
  */
 def captureStdOutStdErr[T](os: OutputStream)(f: => T): T = {
   captureScalaStdOutStdErr(os){ captureJavaStdOutStdErr(os)(f) }
 }

 /**
  * Capture Java's Standard Out and Standard Error to an Output Stream
  */
 private def captureJavaStdOutStdErr[T](os: OutputStream)(f: => T): T = {
   val out: PrintStream = System.out
   val err: PrintStream = System.err

   try {
     System.setOut(new PrintStream(new TeeOutputStream(out, os), true, "UTF-8"))
     System.setErr(new PrintStream(new TeeOutputStream(err, os), true, "UTF-8"))

     f
   } finally {
     System.setOut(out)
     System.setErr(err)
   }
 }

 /**
  * Capture Scala's Standard Out and Standard Error (Console.out and Console.err) to an Output Stream
  */
 private def captureScalaStdOutStdErr[T](os: OutputStream)(f: => T): T = {
   Console.withOut(new PrintStream(new TeeOutputStream(Console.out, os), true, "UTF-8")) {
     Console.withErr(new PrintStream(new TeeOutputStream(Console.err, os), true, "UTF-8")) {
       f
     }
   }
 }

 private def withAppender[T](logger: ch.qos.logback.classic.Logger, appender: ch.qos.logback.core.Appender[ch.qos.logback.classic.spi.ILoggingEvent])(fun: => T): T = {
   try {
     logger.addAppender(appender)
     fun
   } finally {
     logger.detachAppender(appender)
   }
 }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy