Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Demonstration Only] Cross-Version Support for Scala 2 and Scala 3 #350

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
fail-fast: false
matrix:
java: [8, 11, 17]
scala: [2.11.12, 2.12.15, 2.13.8, 3.0.2]
scala: [2.13.8, 3.0.2]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand Down
39 changes: 39 additions & 0 deletions app2/src/test/scala/com/typesafe/scalalogging/Scala2Spec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.typesafe.scalalogging

import org.mockito.Mockito._
import org.scalatest.wordspec.AnyWordSpec
import org.scalatestplus.mockito.MockitoSugar
import org.slf4j.{ Logger => Underlying }

class Scala2 extends AnyWordSpec with MockitoSugar {

"Calling Logger from Scala 2" should {
"work" in {
val f = fixture(_.isInfoEnabled, isEnabled = true)
import f._

logger.info("message")
verify(underlying).info("message")
}
}

private def fixture(p: Underlying => Boolean, isEnabled: Boolean) = new LoggerF(p, isEnabled)
private class LoggerF(p: Underlying => Boolean, isEnabled: Boolean) {
val msg = "msg"
val cause = new RuntimeException("cause")
val arg1 = "arg1"
val arg2 = Integer.valueOf(1)
val arg3 = "arg3"
val arg4 = 4
val arg4ref = arg4.asInstanceOf[AnyRef]
val arg5 = true
val arg5ref = arg5.asInstanceOf[AnyRef]
val arg6 = 6L
val arg6ref = arg6.asInstanceOf[AnyRef]
val arg7 = new Throwable
val arg7ref = arg7.asInstanceOf[AnyRef]
val underlying = mock[org.slf4j.Logger]
when(p(underlying)).thenReturn(isEnabled)
val logger = Logger(underlying)
}
}
39 changes: 39 additions & 0 deletions app3/src/test/scala/com/typesafe/scalalogging/Scala3Spec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.typesafe.scalalogging

import org.mockito.Mockito._
import org.scalatest.wordspec.AnyWordSpec
import org.scalatestplus.mockito.MockitoSugar
import org.slf4j.{ Logger => Underlying }

class Scala3 extends AnyWordSpec with MockitoSugar {

"Calling Logger from Scala 3" should {
"work" in {
val f = fixture(_.isInfoEnabled, isEnabled = true)
import f._

logger.info("message")
verify(underlying).info("message")
}
}

private def fixture(p: Underlying => Boolean, isEnabled: Boolean) = new LoggerF(p, isEnabled)
private class LoggerF(p: Underlying => Boolean, isEnabled: Boolean) {
val msg = "msg"
val cause = new RuntimeException("cause")
val arg1 = "arg1"
val arg2 = Integer.valueOf(1)
val arg3 = "arg3"
val arg4 = 4
val arg4ref = arg4.asInstanceOf[AnyRef]
val arg5 = true
val arg5ref = arg5.asInstanceOf[AnyRef]
val arg6 = 6L
val arg6ref = arg6.asInstanceOf[AnyRef]
val arg7 = new Throwable
val arg7ref = arg7.asInstanceOf[AnyRef]
val underlying = mock[org.slf4j.Logger]
when(p(underlying)).thenReturn(isEnabled)
val logger = Logger(underlying)
}
}
87 changes: 37 additions & 50 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,55 +1,42 @@
// basics
ThisBuild / scalaVersion := "3.0.2"
val scala213 = "2.13.8"

name := "scala-logging"
crossScalaVersions := Seq("3.0.2", "2.11.12", "2.12.15", "2.13.8")
scalaVersion := crossScalaVersions.value.head
ThisBuild / versionScheme := Some("early-semver")
scalacOptions ++= Seq(
"-unchecked",
"-deprecation",
"-language:_",
"-encoding", "UTF-8",
"-Ywarn-unused"
)
incOptions := incOptions.value.withLogRecompileOnMacro(false)
val isScala3 = Def.setting {
CrossVersion.partialVersion(scalaVersion.value).exists(_._1 != 2)
}
libraryDependencies ++= Dependencies.scalaLogging(scalaVersion.value, isScala3.value)
initialCommands := """|import com.typesafe.scalalogging._
|import org.slf4j.{ Logger => Underlying, _ }""".stripMargin

// OSGi
lazy val scalalogging2 = project
.settings(
name := "scala-logging2",
scalaVersion := scala213,
libraryDependencies ++= Dependencies.scalaLogging(scala213, false)
)

import com.typesafe.sbt.osgi.SbtOsgi
enablePlugins(SbtOsgi)
osgiSettings
OsgiKeys.bundleSymbolicName := "com.typesafe.scala-logging"
OsgiKeys.privatePackage := Seq()
OsgiKeys.exportPackage := Seq("com.typesafe.scalalogging*")
lazy val scalalogging3 = project
.settings(
name := "scala-logging3",
scalaVersion := "3.0.2",
libraryDependencies ++= Dependencies.scalaLogging("3.0.2", true)
)

// publishing
lazy val scalalogging = project
.dependsOn(scalalogging2, scalalogging3)
.settings(
name := "scala-logging",
scalaVersion := "3.0.2",
libraryDependencies ++= Dependencies.scalaLogging("3.0.2", true),
)

organization := "com.typesafe.scala-logging"
sonatypeProfileName := "com.typesafe"
licenses := Seq("Apache 2.0 License" -> url("http://www.apache.org/licenses/LICENSE-2.0.html"))
homepage := Some(url("https://github.com/lightbend/scala-logging"))
Test / publishArtifact := false
pomIncludeRepository := (_ => false)
scmInfo := Some(
ScmInfo(url("https://github.com/lightbend/scala-logging"), "scm:git:[email protected]:lightbend/scala-logging.git")
)
developers := List(
Developer(
id = "hseeberger",
name = "Heiko Seeberger",
email = "",
url = url("http://heikoseeberger.de")
),
Developer(
id = "analytically",
name = "Mathias Bogaert",
email = "",
url = url("http://twitter.com/analytically")
lazy val app2 = project
.dependsOn(scalalogging)
.settings(
name := "app2",
scalaVersion := scala213,
scalacOptions := Seq("-Ytasty-reader"),
libraryDependencies ++= Dependencies.scalaLogging(scala213, false),
libraryDependencies += Library.logbackClassic
)
)

lazy val app3 = project
.dependsOn(scalalogging)
.settings(
name := "app3",
libraryDependencies ++= Dependencies.scalaLogging("3.0.2", true),
libraryDependencies += Library.logbackClassic
)
4 changes: 2 additions & 2 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import sbt._
object Version {
val logback = "1.2.10"
val mockito = "3.2.10.0"
val scalaTest = "3.2.11"
val slf4j = "1.7.36"
val scalaTest = "3.2.10"
val slf4j = "1.7.33"
}

object Library {
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.6.2
sbt.version=1.6.1
3 changes: 0 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3")
addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.9.6")
addSbtPlugin("com.scalapenos" % "sbt-prompt" % "1.0.2")
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.10")
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ object Logger {
/**
* Create a [[LoggerTakingImplicit]] wrapping the given underlying `org.slf4j.Logger`.
*/
def takingImplicit[A](underlying: Underlying)(implicit ev: CanLog[A]): LoggerTakingImplicit[A] =
new LoggerTakingImplicit[A](underlying)
// def takingImplicit[A](underlying: Underlying)(implicit ev: CanLog[A]): LoggerTakingImplicit[A] =
// new LoggerTakingImplicit[A](underlying)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be related to scala/scala3#16630

Copy link
Author

@srollins-lucid srollins-lucid Oct 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that is the same behavior that caused me to have to remove these methods.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have raised a PR on scalac to fix this bug, however it's not clear if this is the correct fix as I never looked into the compiler before and it seems to be very complicated. scala/scala3#18663


/**
* Create a [[Logger]] for the given name.
Expand All @@ -37,8 +37,8 @@ object Logger {
* val logger = Logger.takingImplicit[CorrelationId]("application")
* }}}
*/
def takingImplicit[A](name: String)(implicit ev: CanLog[A]): LoggerTakingImplicit[A] =
new LoggerTakingImplicit[A](LoggerFactory.getLogger(name))
// def takingImplicit[A](name: String)(implicit ev: CanLog[A]): LoggerTakingImplicit[A] =
// new LoggerTakingImplicit[A](LoggerFactory.getLogger(name))

/**
* Create a [[Logger]] wrapping the created underlying `org.slf4j.Logger`.
Expand All @@ -49,8 +49,8 @@ object Logger {
/**
* Create a [[LoggerTakingImplicit]] wrapping the created underlying `org.slf4j.Logger`.
*/
def takingImplicit[A](clazz: Class[_])(implicit ev: CanLog[A]): LoggerTakingImplicit[A] =
new LoggerTakingImplicit[A](LoggerFactory.getLogger(clazz.getName))
// def takingImplicit[A](clazz: Class[_])(implicit ev: CanLog[A]): LoggerTakingImplicit[A] =
// new LoggerTakingImplicit[A](LoggerFactory.getLogger(clazz.getName))

/**
* Create a [[Logger]] for the runtime class wrapped by the implicit class
Expand All @@ -71,8 +71,8 @@ object Logger {
* val logger = Logger.takingImplicit[MyClass, CorrelationId]
* }}}
*/
def takingImplicit[T, A](implicit ct: ClassTag[T], ev: CanLog[A]): LoggerTakingImplicit[A] =
new LoggerTakingImplicit[A](LoggerFactory.getLogger(ct.runtimeClass.getName.stripSuffix("$")))
// def takingImplicit[T, A](implicit ct: ClassTag[T], ev: CanLog[A]): LoggerTakingImplicit[A] =
// new LoggerTakingImplicit[A](LoggerFactory.getLogger(ct.runtimeClass.getName.stripSuffix("$")))
}

/**
Expand Down
126 changes: 126 additions & 0 deletions scalalogging/src/main/scala/com/typesafe/scalalogging/LoggerImpl.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package com.typesafe.scalalogging

import com.typesafe.scalalogging.{LoggerMacro2, LoggerMacro3}

import org.slf4j.{Marker, Logger as Underlying }

trait LoggerImpl {
def underlying: Underlying

import scala.language.experimental.macros

// Error

def error(message: String): Unit = macro LoggerMacro2.errorMessage
inline def error(inline message: String): Unit = ${LoggerMacro3.errorMessage('underlying, 'message)}

def error(message: String, cause: Throwable): Unit = macro LoggerMacro2.errorMessageCause
inline def error(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.errorMessageCause('underlying, 'message, 'cause)}

def error(message: String, args: Any*): Unit = macro LoggerMacro2.errorMessageArgs
inline def error(inline message: String, inline args: Any*): Unit = ${LoggerMacro3.errorMessageArgs('underlying, 'message, 'args)}

def error(marker: Marker, message: String): Unit = macro LoggerMacro2.errorMessageMarker
inline def error(inline marker: Marker, inline message: String): Unit = ${LoggerMacro3.errorMessageMarker('underlying, 'marker, 'message)}

def error(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro2.errorMessageCauseMarker
inline def error(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.errorMessageCauseMarker('underlying, 'marker, 'message, 'cause)}

def error(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro2.errorMessageArgsMarker
inline def error(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro3.errorMessageArgsMarker('underlying, 'marker, 'message, 'args)}

def whenErrorEnabled(body: Unit): Unit = macro LoggerMacro2.errorCode
inline def whenErrorEnabled(inline body: Unit): Unit = ${LoggerMacro3.errorCode('underlying, 'body)}

// Warn

def warn(message: String): Unit = macro LoggerMacro2.warnMessage
inline def warn(inline message: String): Unit = ${LoggerMacro3.warnMessage('underlying, 'message)}

def warn(message: String, cause: Throwable): Unit = macro LoggerMacro2.warnMessageCause
inline def warn(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.warnMessageCause('underlying, 'message, 'cause)}

def warn(message: String, args: Any*): Unit = macro LoggerMacro2.warnMessageArgs
inline def warn(inline message: String, inline args: Any*): Unit = ${LoggerMacro3.warnMessageArgs('underlying, 'message, 'args)}

def warn(marker: Marker, message: String): Unit = macro LoggerMacro2.warnMessageMarker
inline def warn(inline marker: Marker, inline message: String): Unit = ${LoggerMacro3.warnMessageMarker('underlying, 'marker, 'message)}

def warn(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro2.warnMessageCauseMarker
inline def warn(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.warnMessageCauseMarker('underlying, 'marker, 'message, 'cause)}

def warn(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro2.warnMessageArgsMarker
inline def warn(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro3.warnMessageArgsMarker('underlying, 'marker, 'message, 'args)}

def whenWarnEnabled(body: Unit): Unit = macro LoggerMacro2.warnCode
inline def whenWarnEnabled(inline body: Unit): Unit = ${LoggerMacro3.warnCode('underlying, 'body)}

// Info

def info(message: String): Unit = macro LoggerMacro2.infoMessage
inline def info(inline message: String): Unit = ${LoggerMacro3.infoMessage('underlying, 'message)}

def info(message: String, cause: Throwable): Unit = macro LoggerMacro2.infoMessageCause
inline def info(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.infoMessageCause('underlying, 'message, 'cause)}

def info(message: String, args: Any*): Unit = macro LoggerMacro2.infoMessageArgs
inline def info(inline message: String, inline args: Any*): Unit = ${LoggerMacro3.infoMessageArgs('underlying, 'message, 'args)}

def info(marker: Marker, message: String): Unit = macro LoggerMacro2.infoMessageMarker
inline def info(inline marker: Marker, inline message: String): Unit = ${LoggerMacro3.infoMessageMarker('underlying, 'marker, 'message)}

def info(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro2.infoMessageCauseMarker
inline def info(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.infoMessageCauseMarker('underlying, 'marker, 'message, 'cause)}

def info(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro2.infoMessageArgsMarker
inline def info(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro3.infoMessageArgsMarker('underlying, 'marker, 'message, 'args)}

def whenInfoEnabled(body: Unit): Unit = macro LoggerMacro2.infoCode
inline def whenInfoEnabled(inline body: Unit): Unit = ${LoggerMacro3.infoCode('underlying, 'body)}

// Debug

def debug(message: String): Unit = macro LoggerMacro2.debugMessage
inline def debug(inline message: String): Unit = ${LoggerMacro3.debugMessage('underlying, 'message)}

def debug(message: String, cause: Throwable): Unit = macro LoggerMacro2.debugMessageCause
inline def debug(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.debugMessageCause('underlying, 'message, 'cause)}

def debug(message: String, args: Any*): Unit = macro LoggerMacro2.debugMessageArgs
inline def debug(inline message: String, inline args: Any*): Unit = ${LoggerMacro3.debugMessageArgs('underlying, 'message, 'args)}

def debug(marker: Marker, message: String): Unit = macro LoggerMacro2.debugMessageMarker
inline def debug(inline marker: Marker, inline message: String): Unit = ${LoggerMacro3.debugMessageMarker('underlying, 'marker, 'message)}

def debug(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro2.debugMessageCauseMarker
inline def debug(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.debugMessageCauseMarker('underlying, 'marker, 'message, 'cause)}

def debug(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro2.debugMessageArgsMarker
inline def debug(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro3.debugMessageArgsMarker('underlying, 'marker, 'message, 'args)}

def whenDebugEnabled(body: Unit): Unit = macro LoggerMacro2.debugCode
inline def whenDebugEnabled(inline body: Unit): Unit = ${LoggerMacro3.debugCode('underlying, 'body)}

// Trace

def trace(message: String): Unit = macro LoggerMacro2.traceMessage
inline def trace(inline message: String): Unit = ${LoggerMacro3.traceMessage('underlying, 'message)}

def trace(message: String, cause: Throwable): Unit = macro LoggerMacro2.traceMessageCause
inline def trace(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.traceMessageCause('underlying, 'message, 'cause)}

def trace(message: String, args: Any*): Unit = macro LoggerMacro2.traceMessageArgs
inline def trace(inline message: String, inline args: Any*): Unit = ${LoggerMacro3.traceMessageArgs('underlying, 'message, 'args)}

def trace(marker: Marker, message: String): Unit = macro LoggerMacro2.traceMessageMarker
inline def trace(inline marker: Marker, inline message: String): Unit = ${LoggerMacro3.traceMessageMarker('underlying, 'marker, 'message)}

def trace(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro2.traceMessageCauseMarker
inline def trace(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro3.traceMessageCauseMarker('underlying, 'marker, 'message, 'cause)}

def trace(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro2.traceMessageArgsMarker
inline def trace(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro3.traceMessageArgsMarker('underlying, 'marker, 'message, 'args)}

def whenTraceEnabled(body: Unit): Unit = macro LoggerMacro2.traceCode
inline def whenTraceEnabled(inline body: Unit): Unit = ${LoggerMacro3.traceCode('underlying, 'body)}
}
Loading