-
Notifications
You must be signed in to change notification settings - Fork 73
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
Add LoggerFactory typeclass #629
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
2917dc4
Add LoggerFactory typeclass
hamnis 9f95c60
Restore binary compatibility
hamnis 237566a
rename internal macro
hamnis e9df149
Add headers
hamnis b63b289
Add LoggernameTest
hamnis 12db08b
Add missing headers
hamnis 625d5a6
Changes based on feedback
hamnis c45fe56
Fix headers again
hamnis 9d2421d
Slf4jFactory as toplevel trait
hamnis 7167e54
The Companion trait did not work as expected
hamnis d2c3d64
Revert "The Companion trait did not work as expected"
hamnis 5fef304
Companion trait worked, the implicit did not resolve
hamnis 9fc531a
Add headers again
hamnis 2d2e74d
Exploit path dependent types and get generic companion type to work
hamnis 688e205
Fix formatting
hamnis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}]. | ||
Information can be found here: https://log4cats.github.io/logging-capability.html | ||
""") | ||
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can consider dropping this, since LoggerFactoryGen now has everything that is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's kinda convinient for users who would like to use just
LoggerFactory
without the need to define LoggerType explicitly