-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #656 from FunFunFine/series/1.x-backport-logger-fa…
…ctory Backport LoggerFactory to 1.x series
- Loading branch information
Showing
18 changed files
with
526 additions
and
200 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
core/shared/src/main/scala-2/org/typelevel/log4cats/LoggerNameCompat.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* 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 org.typelevel.log4cats | ||
|
||
import org.typelevel.log4cats.internal.LoggerNameMacro | ||
|
||
trait LoggerNameCompat { | ||
implicit def name: LoggerName = macro LoggerNameMacro.getLoggerName | ||
} |
105 changes: 105 additions & 0 deletions
105
core/shared/src/main/scala-2/org/typelevel/log4cats/internal/LoggerNameMacro.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* 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 org.typelevel.log4cats.internal | ||
|
||
import scala.annotation.tailrec | ||
import scala.reflect.macros.blackbox | ||
|
||
private[log4cats] class LoggerNameMacro(val c: blackbox.Context) { | ||
final def getLoggerName = SharedLoggerNameMacro.getLoggerNameImpl(c) | ||
} | ||
|
||
private[log4cats] object SharedLoggerNameMacro { | ||
|
||
/** Get a logger by reflecting the enclosing class name. */ | ||
private[log4cats] def getLoggerNameImpl(c: blackbox.Context) = { | ||
import c.universe._ | ||
|
||
@tailrec def findEnclosingClass(sym: c.universe.Symbol): c.universe.Symbol = { | ||
sym match { | ||
case NoSymbol => | ||
c.abort(c.enclosingPosition, s"Couldn't find an enclosing class or module for the logger") | ||
case s if s.isModule || s.isClass => | ||
s | ||
case other => | ||
/* We're not in a module or a class, so we're probably inside a member definition. Recurse upward. */ | ||
findEnclosingClass(other.owner) | ||
} | ||
} | ||
|
||
val cls = findEnclosingClass(c.internal.enclosingOwner) | ||
|
||
assert(cls.isModule || cls.isClass, "Enclosing class is always either a module or a class") | ||
|
||
def nameBySymbol(s: Symbol) = { | ||
def fullName(s: Symbol): String = { | ||
@inline def isPackageObject = ( | ||
(s.isModule || s.isModuleClass) | ||
&& s.owner.isPackage | ||
&& s.name.decodedName.toString == termNames.PACKAGE.decodedName.toString | ||
) | ||
|
||
if (s.isModule || s.isClass) { | ||
if (isPackageObject) { | ||
s.owner.fullName | ||
} else if (s.owner.isStatic) { | ||
s.fullName | ||
} else { | ||
fullName(s.owner) + "." + s.name.encodedName.toString | ||
} | ||
} else { | ||
fullName(s.owner) | ||
} | ||
} | ||
|
||
q"new _root_.org.typelevel.log4cats.LoggerName(${fullName(s)})" | ||
} | ||
|
||
def nameByType(s: Symbol) = { | ||
val typeSymbol: ClassSymbol = (if (s.isModule) s.asModule.moduleClass else s).asClass | ||
val typeParams = typeSymbol.typeParams | ||
|
||
if (typeParams.isEmpty) { | ||
q"new _root_.org.typelevel.log4cats.LoggerName(_root_.scala.Predef.classOf[$typeSymbol].getName)" | ||
} else { | ||
if (typeParams.exists(_.asType.typeParams.nonEmpty)) { | ||
/* We have at least one higher-kinded type: fall back to by-name logger construction, as | ||
* there's no simple way to declare a higher-kinded type with an "any" parameter. */ | ||
nameBySymbol(s) | ||
} else { | ||
val typeArgs = List.fill(typeParams.length)(WildcardType) | ||
val typeConstructor = tq"$typeSymbol[..${typeArgs}]" | ||
q"new _root_.org.typelevel.log4cats.LoggerName(_root_.scala.Predef.classOf[$typeConstructor].getName)" | ||
} | ||
} | ||
} | ||
|
||
@inline def isInnerClass(s: Symbol) = | ||
s.isClass && !(s.owner.isPackage) | ||
|
||
val instanceByName = | ||
(true && cls.isModule || cls.isModuleClass) || (cls.isClass && isInnerClass( | ||
cls | ||
)) | ||
|
||
if (instanceByName) { | ||
nameBySymbol(cls) | ||
} else { | ||
nameByType(cls) | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
core/shared/src/main/scala-3/org/typelevel/log4cats/LoggerNameCompat.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* 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 org.typelevel.log4cats | ||
|
||
import org.typelevel.log4cats.internal.LoggerNameMacro | ||
|
||
import scala.quoted.* | ||
|
||
trait LoggerNameCompat { | ||
implicit inline def name: LoggerName = | ||
${ LoggerNameMacro.getLoggerName } | ||
} |
71 changes: 71 additions & 0 deletions
71
core/shared/src/main/scala-3/org/typelevel/log4cats/internal/LoggerNameMacro.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* 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 org.typelevel.log4cats | ||
package internal | ||
|
||
import scala.annotation.tailrec | ||
import scala.quoted.* | ||
|
||
private[log4cats] object LoggerNameMacro { | ||
def getLoggerName(using qctx: Quotes): Expr[LoggerName] = { | ||
val name = getLoggerNameImpl | ||
'{ new LoggerName($name) } | ||
} | ||
|
||
def getLoggerNameImpl(using qctx: Quotes): Expr[String] = { | ||
import qctx.reflect._ | ||
|
||
@tailrec def findEnclosingClass(sym: Symbol): Symbol = { | ||
sym match { | ||
case s if s.isNoSymbol => | ||
report.throwError("Couldn't find an enclosing class or module for the logger") | ||
case s if s.isClassDef => | ||
s | ||
case other => | ||
/* We're not in a module or a class, so we're probably inside a member definition. Recurse upward. */ | ||
findEnclosingClass(other.owner) | ||
} | ||
} | ||
|
||
def symbolName(s: Symbol): Expr[String] = { | ||
def fullName(s: Symbol): String = { | ||
val flags = s.flags | ||
if (flags.is(Flags.Package)) { | ||
s.fullName | ||
} else if (s.isClassDef) { | ||
if (flags.is(Flags.Module)) { | ||
if (s.name == "package$") { | ||
fullName(s.owner) | ||
} else { | ||
val chomped = s.name.stripSuffix("$") | ||
fullName(s.owner) + "." + chomped | ||
} | ||
} else { | ||
fullName(s.owner) + "." + s.name | ||
} | ||
} else { | ||
fullName(s.owner) | ||
} | ||
} | ||
|
||
Expr(fullName(s).stripSuffix("$")) | ||
} | ||
|
||
val cls = findEnclosingClass(Symbol.spliceOwner) | ||
symbolName(cls) | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
core/shared/src/main/scala/org/typelevel/log4cats/LoggerFactory.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* 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 org.typelevel.log4cats | ||
|
||
import scala.annotation.implicitNotFound | ||
|
||
@implicitNotFound(""" | ||
Implicit not found for LoggerFactory[${F}]. | ||
Learn about LoggerFactory at https://typelevel.org/log4cats/#logging-using-capabilities | ||
""") | ||
trait LoggerFactory[F[_]] extends LoggerFactoryGen[F] { | ||
type LoggerType = SelfAwareStructuredLogger[F] | ||
} | ||
|
||
object LoggerFactory extends LoggerFactoryGenCompanion { | ||
def apply[F[_]: LoggerFactory]: LoggerFactory[F] = implicitly | ||
} |
42 changes: 42 additions & 0 deletions
42
core/shared/src/main/scala/org/typelevel/log4cats/LoggerFactoryGen.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* 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 org.typelevel.log4cats | ||
|
||
trait LoggerFactoryGen[F[_]] { | ||
type LoggerType <: Logger[F] | ||
def getLogger(implicit name: LoggerName): LoggerType = getLoggerFromName(name.value) | ||
def getLoggerFromClass(clazz: Class[_]): LoggerType = getLoggerFromName(clazz.getName) | ||
def create(implicit name: LoggerName): F[LoggerType] = fromName(name.value) | ||
def fromClass(clazz: Class[_]): F[LoggerType] = fromName(clazz.getName) | ||
def getLoggerFromName(name: String): LoggerType | ||
def fromName(name: String): F[LoggerType] | ||
} | ||
|
||
private[log4cats] trait LoggerFactoryGenCompanion { | ||
def getLogger[F[_]](implicit lf: LoggerFactoryGen[F], name: LoggerName): lf.LoggerType = | ||
lf.getLogger | ||
def getLoggerFromName[F[_]](name: String)(implicit lf: LoggerFactoryGen[F]): lf.LoggerType = | ||
lf.getLoggerFromName(name) | ||
def getLoggerFromClass[F[_]](clazz: Class[_])(implicit lf: LoggerFactoryGen[F]): lf.LoggerType = | ||
lf.getLoggerFromClass(clazz) | ||
def create[F[_]](implicit lf: LoggerFactoryGen[F], name: LoggerName): F[lf.LoggerType] = | ||
lf.create | ||
def fromName[F[_]](name: String)(implicit lf: LoggerFactoryGen[F]): F[lf.LoggerType] = | ||
lf.fromName(name) | ||
def fromClass[F[_]](clazz: Class[_])(implicit lf: LoggerFactoryGen[F]): F[lf.LoggerType] = | ||
lf.fromClass(clazz) | ||
} |
21 changes: 21 additions & 0 deletions
21
core/shared/src/main/scala/org/typelevel/log4cats/LoggerName.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* 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 org.typelevel.log4cats | ||
|
||
object LoggerName extends LoggerNameCompat | ||
|
||
final case class LoggerName(value: String) extends AnyVal |
37 changes: 37 additions & 0 deletions
37
core/shared/src/test/scala/org/typelevel/log4cats/LoggerNameTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* Copyright 2018 Typelevel | ||
* | ||
* 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 org.typelevel.log4cats | ||
|
||
import munit.FunSuite | ||
|
||
class LoggerNameTest extends FunSuite { | ||
class FooA { | ||
val name = LoggerName.name | ||
} | ||
|
||
object FooB { | ||
val name = LoggerName.name | ||
} | ||
|
||
test("names") { | ||
val name1 = new FooA().name | ||
val name2 = FooB.name | ||
|
||
assertEquals(name1.value, "org.typelevel.log4cats.LoggerNameTest.FooA") | ||
assertEquals(name2.value, "org.typelevel.log4cats.LoggerNameTest.FooB") | ||
} | ||
} |
Oops, something went wrong.