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

rapture.log.log.scala Maven / Gradle / Ivy

There is a newer version: 2.0.0-M9
Show newest version
/******************************************************************************************************************\
* Rapture Log, version 2.0.0. Copyright 2010-2015 Jon Pretty, Propensive Ltd.                                      *
*                                                                                                                  *
* The primary distribution site is http://rapture.io/                                                              *
*                                                                                                                  *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in complance    *
* 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 rapture.log

import rapture.base._
import rapture.core._
import rapture.io._

import scala.annotation._
import scala.concurrent.Channel

import language.experimental.macros

object `package` {
  val log = Log
  
  implicit class LogStringContext(sc: StringContext) {
    def log(xs: rapture.log.parts.Part*): Spec = {
      new Spec {
        def render(level: Int, lineNo: Int, source: String) = {
          val sb = new StringBuilder
          for(i <- 0 until (sc.parts.length - 1)) {
            sb.append(sc.parts(i))
            sb.append(xs(i).fit(level, lineNo, source))
          }
          sb.append(sc.parts.last)
          sb.toString
        }
      }
    }
  }
}

trait Outputter {
  def log[L <: LogLevel](prefix: String, msg: Array[String])
}

object LogAction {
  implicit def defaultLogAction(implicit act: NamedLogAction): LogAction =
    new LogAction { def level = -1 }
}

@implicitNotFound("You have not specified a valid logging level. Please import one of logLevels"+
    ".{trace._, debug._, info._, warn._, error._, fatal._}.")
trait LogAction {
  def level: Int
}

class NamedLogAction(val level: Int, val name: String) extends LogAction

object Loggable {
  implicit val stringLoggable: Loggable[String] = new Loggable[String] {
    def toArray(msg: String) = Array(msg)
  }

  implicit val exceptionLoggable: Loggable[Throwable] = new Loggable[Throwable] {
    def toArray(msg: Throwable) = Array(msg.toString) ++ msg.getStackTrace.map("        at "+_)
  }
}
trait Loggable[-Msg] { def toArray(msg: Msg): Array[String] }

object SourceContext {
  implicit def sourceContext: SourceContext = macro LogMacros.sourceContextMacro
}
case class SourceContext(lineNo: Int, sourceFile: String)

object LogMacros {
  def logMacro[Msg: c.WeakTypeTag](level: Int)(c: BlackboxContext)(msg: c.Expr[Msg])(log: c.Expr[Log], loggable: c.Expr[Loggable[Msg]], srcCtx: c.Expr[SourceContext]): c.Expr[Unit] = {
    import c.universe._
    val lev = c.Expr[Int](Literal(Constant(level)))
    reify {
      if(log.splice.action.level <= lev.splice) {
        log.splice.out.log(log.splice.spec.render(lev.splice, srcCtx.splice.lineNo,
            srcCtx.splice.sourceFile), loggable.splice.toArray(msg.splice))
      }
    }
  }

  def sourceContextMacro(c: BlackboxContext): c.Expr[SourceContext] = {
    import c.universe._
    val lineNo = c.Expr[Int](Literal(Constant(c.enclosingPosition.line)))
    val sourceFile = c.Expr[String](Literal(Constant(c.enclosingPosition.source.toString)))
    reify { SourceContext(lineNo.splice, sourceFile.splice) }
  }

  def traceMacro[Msg: c.WeakTypeTag](c: BlackboxContext)(msg: c.Expr[Msg])(log: c.Expr[Log],
      loggable: c.Expr[Loggable[Msg]], sourceContext: c.Expr[SourceContext]): c.Expr[Unit] =
    logMacro(0)(c)(msg)(log, loggable, sourceContext)
  
  def debugMacro[Msg: c.WeakTypeTag](c: BlackboxContext)(msg: c.Expr[Msg])(log: c.Expr[Log],
      loggable: c.Expr[Loggable[Msg]], sourceContext: c.Expr[SourceContext]): c.Expr[Unit] =
    logMacro(1)(c)(msg)(log, loggable, sourceContext)
  
  def infoMacro[Msg: c.WeakTypeTag](c: BlackboxContext)(msg: c.Expr[Msg])(log: c.Expr[Log],
      loggable: c.Expr[Loggable[Msg]], sourceContext: c.Expr[SourceContext]): c.Expr[Unit] =
    logMacro(2)(c)(msg)(log, loggable, sourceContext)
  
  def warnMacro[Msg: c.WeakTypeTag](c: BlackboxContext)(msg: c.Expr[Msg])(log: c.Expr[Log],
      loggable: c.Expr[Loggable[Msg]], sourceContext: c.Expr[SourceContext]): c.Expr[Unit] =
    logMacro(3)(c)(msg)(log, loggable, sourceContext)
  
  def errorMacro[Msg: c.WeakTypeTag](c: BlackboxContext)(msg: c.Expr[Msg])(log: c.Expr[Log],
      loggable: c.Expr[Loggable[Msg]], sourceContext: c.Expr[SourceContext]): c.Expr[Unit] =
    logMacro(4)(c)(msg)(log, loggable, sourceContext)
  
  def fatalMacro[Msg: c.WeakTypeTag](c: BlackboxContext)(msg: c.Expr[Msg])(log: c.Expr[Log],
      loggable: c.Expr[Loggable[Msg]], sourceContext: c.Expr[SourceContext]): c.Expr[Unit] =
    logMacro(5)(c)(msg)(log, loggable, sourceContext)
}

abstract class Spec {
  def render(level: Int, lineNo: Int, source: String): String
}

/*object Main extends App {
  import logLevels.debug._
  import encodings.system
  implicit val output = Logger(Stdout)
  implicit val spec = log"  "
  Log.logImplicit
  log.info("Here is an information message.")
  sys.exit(1)
}*/

object Log {
  implicit def logImplicit(implicit spec: Spec, out: Outputter, action: LogAction) =
    Log(spec, out, action)
  
  def trace[Msg](msg: Msg)(implicit log: Log, loggable: Loggable[Msg],
      sourceContext: SourceContext): Unit = macro LogMacros.traceMacro[Msg]
  
  def debug[Msg](msg: Msg)(implicit log: Log, loggable: Loggable[Msg],
      sourceContext: SourceContext): Unit = macro LogMacros.debugMacro[Msg]
  
  def info[Msg](msg: Msg)(implicit log: Log, loggable: Loggable[Msg],
      sourceContext: SourceContext): Unit = macro LogMacros.infoMacro[Msg]
  
  def warn[Msg](msg: Msg)(implicit log: Log, loggable: Loggable[Msg],
      sourceContext: SourceContext): Unit = macro LogMacros.warnMacro[Msg]
  
  def error[Msg](msg: Msg)(implicit log: Log, loggable: Loggable[Msg],
      sourceContext: SourceContext): Unit = macro LogMacros.errorMacro[Msg]
  
  def fatal[Msg](msg: Msg)(implicit log: Log, loggable: Loggable[Msg],
      sourceContext: SourceContext): Unit = macro LogMacros.fatalMacro[Msg]
}
case class Log(spec: Spec, out: Outputter, action: LogAction)

case class Logger[Res](res: Res)(implicit appender: Appender[Res, String]) extends Outputter {

  final val maxDelay = 25
  final val queueThreshold = 10

  private val queue = new scala.collection.mutable.Queue[String]
  private var blankPrefix: String = null

  private val thread: Thread = Thread.fork("rapture-log") {
    var continue = true
    sys.addShutdownHook { continue = false }

    while(continue) { try {
      queue synchronized {
        queue.wait(maxDelay)
        if(queue.size > 0) res handleAppend { out: Output[String] =>
          while(queue.size > 0) out.write(queue.dequeue)
        }
      }
    } catch { case e: InterruptedException =>
      continue = false
    }
  } }
  
  def log[L <: LogLevel](prefix: String, msg: Array[String]) = queue synchronized {
    if(blankPrefix == null) blankPrefix = (" "*(prefix.length + 1))
    queue.enqueue(prefix+" "+msg(0))
    msg.tail foreach { m => queue.enqueue(blankPrefix+m) }
    if(queue.size >= queueThreshold) queue.notify()
  }
}

object stdoutLogging {
  import parts._
  implicit def implicitSpec(implicit severity: Severity, date: Date, time: Time,
      thread: Thread): Spec =
    log"""$date $time [$severity] ${sourceFile(width = 12, Right)}:${lineNo(4)} ${thread(10)}"""
  
  implicit val output = Logger(Stdout)

  def apply()(implicit severity: Severity, date: Date, time: Time, thread: Thread):
      Spec with Outputter = new Spec with Outputter {
    
    def render(level: Int, lineNo: Int, source: String): String =
      implicitSpec.render(level, lineNo, source)
    
    def log[L <: LogLevel](prefix: String, msg: Array[String]) = output.log(prefix, msg)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy