├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── build.sbt ├── project ├── Dependencies.scala ├── build.properties └── plugins.sbt ├── scalariform.sbt └── src ├── main ├── resources │ └── LICENSE-2.0.txt ├── scala-2 │ └── com │ │ └── typesafe │ │ └── scalalogging │ │ ├── LoggerImpl.scala │ │ ├── LoggerMacro.scala │ │ ├── LoggerTakingImplicitImpl.scala │ │ └── LoggerTakingImplicitMacro.scala ├── scala-3 │ └── com │ │ └── typesafe │ │ └── scalalogging │ │ ├── LoggerImpl.scala │ │ ├── LoggerMacro.scala │ │ ├── LoggerTakingImplicitImpl.scala │ │ └── LoggerTakingImplicitMacro.scala └── scala │ └── com │ └── typesafe │ └── scalalogging │ ├── Logger.scala │ ├── LoggerTakingImplicit.scala │ ├── Logging.scala │ └── package.scala └── test ├── resources └── logback.xml └── scala └── com └── typesafe └── scalalogging ├── LoggerSpec.scala ├── LoggerTakingImplicitSpec.scala ├── LoggerWithMarkerSpec.scala └── LoggerWithTaggedArgsSpec.scala /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | jobs: 8 | test: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | java: [8, 11, 17] 13 | scala: [2.11.x, 2.12.x, 2.13.x, 3.x] 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | - uses: coursier/cache-action@v6 20 | - uses: actions/setup-java@v3 21 | with: 22 | distribution: temurin 23 | java-version: ${{matrix.java}} 24 | - uses: sbt/setup-sbt@v1 25 | - name: Test 26 | run: | 27 | sbt ++${{matrix.scala}} test 28 | git diff --exit-code # check scalariform 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: ["*"] 5 | jobs: 6 | publish: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | with: 11 | fetch-depth: 0 12 | - uses: actions/setup-java@v3 13 | with: 14 | distribution: temurin 15 | java-version: 8 16 | - uses: sbt/setup-sbt@v1 17 | - run: sbt ci-release 18 | env: 19 | PGP_PASSPHRASE: ${{secrets.PGP_PASSPHRASE}} 20 | PGP_SECRET: ${{secrets.PGP_SECRET}} 21 | SONATYPE_PASSWORD: ${{secrets.SONATYPE_PASSWORD}} 22 | SONATYPE_USERNAME: ${{secrets.SONATYPE_USERNAME}} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # sbt 2 | lib_managed/ 3 | project/project 4 | target/ 5 | .bsp 6 | 7 | # Worksheets (Eclipse or IntelliJ) 8 | *.sc 9 | 10 | # Eclipse 11 | .cache 12 | .classpath 13 | .project 14 | .scala_dependencies 15 | .settings 16 | .target/ 17 | .worksheet/ 18 | 19 | # IntelliJ 20 | .idea/ 21 | 22 | # VSCode 23 | .bloop/ 24 | .metals/ 25 | project/metals.sbt 26 | 27 | # ENSIME 28 | .ensime 29 | .ensime_lucene/ 30 | 31 | # Mac 32 | .DS_Store 33 | 34 | # Akka Persistence 35 | journal/ 36 | snapshots/ 37 | 38 | # Log files 39 | *.log 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2014-2025 Lightbend, Inc. dba Akka 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Scala Logging is a **convenient** and **fast** logging library wrapping [SLF4J](http://www.slf4j.org). 2 | 3 | It's convenient, because you can simply call log methods, *without* checking whether the respective log level is enabled: 4 | 5 | ```scala 6 | logger.debug(s"Some $expensive message!") 7 | ``` 8 | 9 | It's fast, because thanks to Scala macros the *check-enabled-idiom* is applied and the following code is generated: 10 | 11 | ```scala 12 | if (logger.isDebugEnabled) logger.debug(s"Some $expensive message!") 13 | ``` 14 | 15 | ## Prerequisites 16 | 17 | * Java 8 or higher 18 | * Scala 2.11, 2.12, 2.13 or 3.0 19 | * Logging backend compatible with SLF4J 20 | 21 | A compatible logging backend is [Logback](http://logback.qos.ch), add it to your sbt build definition: 22 | 23 | ```scala 24 | libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.10" 25 | ``` 26 | 27 | If you are looking for a version compatible with Scala 2.10, check out Scala Logging 2.x. 28 | 29 | ## Getting Scala Logging 30 | 31 | Scala Logging is published to Sonatype OSS and Maven Central: 32 | 33 | - Group id / organization: *com.typesafe.scala-logging* 34 | - Artifact id / name: *scala-logging* 35 | 36 | sbt users may add this to their `build.sbt`: 37 | 38 | ```scala 39 | libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.4" 40 | ``` 41 | 42 | ## Using Scala Logging 43 | 44 | The `Logger` class from the `com.typesafe.scalalogging` package wraps an underlying SLF4J logger. 45 | In order to create a `Logger`, you pass a name to the `apply` factory method defined in the `Logger` companion object: 46 | 47 | ```scala 48 | val logger = Logger("name") 49 | ``` 50 | 51 | Or, you pass in a SLF4J logger instance: 52 | 53 | ```scala 54 | val logger = Logger(LoggerFactory.getLogger("name")) 55 | ``` 56 | 57 | Or, you pass in the name of the class into which it is defined: 58 | 59 | ```scala 60 | val logger = Logger(getClass.getName) 61 | ``` 62 | 63 | Or, you pass in a class: 64 | 65 | ```scala 66 | val logger = Logger(classOf[MyClass]) 67 | ``` 68 | 69 | Or, using the runtime class wrapped by the implicit class tag parameter: 70 | 71 | ```scala 72 | val logger = Logger[MyClass] 73 | ``` 74 | 75 | The `LazyLogging` and `StrictLogging` traits from the `com.typesafe.scalalogging` package define the `logger` member as 76 | a lazy or strict value respectively, whereas the `AnyLogging` trait defines an abstract `logger`. 77 | 78 | It depends on the individual use case which trait to use. However, we have defined some scenarios where you can use these traits: 79 | 80 | - Use `LazyLogging` if you are creating lots of objects with this trait repetitively. 81 | - Use `StrictLogging` pretty much by default, especially if the class is a singleton, or you know the log methods will always be invoked. 82 | - Use `AnyLogging` when writing some trait which needs access to any logger without deciding on a specific implementation. 83 | 84 | In case of `LazyLogging` and `StrictLogging`, the underlying SLF4J logger is named according to the class into which 85 | these traits are mixed: 86 | 87 | ```scala 88 | class LazyLoggingExample extends LazyLogging { 89 | logger.debug("This is Lazy Logging ;-)") 90 | 91 | logger.whenDebugEnabled { 92 | println("This would only execute when the debug level is enabled.") 93 | (1 to 10).foreach(x => println("Scala logging is great!")) 94 | } 95 | } 96 | ``` 97 | 98 | ```scala 99 | class StrictLoggingExample extends StrictLogging { 100 | logger.debug("This is Strict Logging ;-)") 101 | 102 | logger.whenDebugEnabled { 103 | println("This would only execute when the debug level is enabled.") 104 | (1 to 10).foreach(x => println("Scala logging is great!")) 105 | } 106 | } 107 | ``` 108 | 109 | ```scala 110 | class AnyLoggingExample extends AnyLogging { 111 | override protected val logger: Logger = Logger("name") 112 | 113 | logger.info("This is Any Logging ;-)") 114 | 115 | logger.whenInfoEnabled { 116 | println("This would only execute when the info level is enabled.") 117 | (1 to 10).foreach(x => println("Scala logging is great!")) 118 | } 119 | } 120 | ``` 121 | 122 | `LoggerTakingImplicit` provides the same methods as `Logger` class, but with additional implicit parameter `A`. 123 | During creation of the `LoggerTakingImplicit` evidence `CanLog[A]` is required. 124 | It may be useful when contextual parameter (e.g. _Correlation ID_) is being passed around and you would like to include it in the log messages: 125 | 126 | ```scala 127 | case class CorrelationId(value: String) 128 | implicit case object CanLogCorrelationId extends CanLog[CorrelationId] { 129 | override def logMessage(originalMsg: String, a: CorrelationId): String = s"${a.value} $originalMsg" 130 | } 131 | 132 | implicit val correlationId = CorrelationId("ID") 133 | 134 | val logger = Logger.takingImplicit[CorrelationId]("test") 135 | logger.info("Test") // takes implicit correlationId and logs "ID Test" 136 | ``` 137 | 138 | If you want to extract the context object associated with your logger i.e. `correlationId` here, use `getContext`. 139 | ```scala 140 | val context = logger.canLogEv.getContext() 141 | ``` 142 | 143 | It's also possible to use `MDC` through `CanLog` without any troubles with execution context. 144 | 145 | ```scala 146 | case class CorrelationId(value: String) 147 | implicit case object CanLogCorrelationId extends CanLog[CorrelationId] { 148 | override def logMessage(originalMsg: String, a: CorrelationId): String = { 149 | MDC.put("correlationId", a.value) 150 | originalMsg 151 | } 152 | 153 | override def afterLog(a: CorrelationId): Unit = { 154 | MDC.remove("correlationId") 155 | } 156 | } 157 | 158 | implicit val correlationId = CorrelationId("ID") 159 | 160 | val logger = Logger.takingImplicit[CorrelationId]("test") 161 | 162 | def serviceMethod(implicit correlationId: CorrelationId): Future[Result] = { 163 | dbCall.map { value => 164 | logger.trace(s"Received value $value from db") // takes implicit correlationId 165 | toResult(value) 166 | } 167 | } 168 | ``` 169 | 170 | ## String Interpolation 171 | 172 | It is idiomatic to use Scala's string interpolation `logger.error(s"log $value")` instead of SLF4J string interpolation `logger.error("log {}", value)`. 173 | However there are some tools (such as [Sentry](https://sentry.io)) that use the log message format as grouping key. Therefore they do not work well with 174 | Scala's string interpolation. 175 | 176 | Scala Logging replaces simple string interpolations with their SLF4J counterparts like this: 177 | 178 | ```scala 179 | logger.error(s"my log message: $arg1 $arg2 $arg3") 180 | ``` 181 | 182 | ```scala 183 | logger.error("my log message: {} {} {}", arg1, arg2, arg3) 184 | ``` 185 | 186 | This has no effect on behavior and performace should be comparable (depends on the underlying logging library). 187 | 188 | ### Limitations 189 | 190 | - Works only when string interpolation is directly used inside the logging statement. That is when the log message is static (available at compile time). 191 | - Works only for the `logger.(message)` and `logger.(marker, message)` logging methods. It does not work if you want to log an exception and 192 | use string interpolation too (this is a limitation of the SLF4J API). 193 | 194 | ## Line numbers in log message? 195 | 196 | Using the [sourcecode](https://github.com/lihaoyi/sourcecode#logging) library, it's possible to add line number 197 | information (especially useful for debugging): 198 | 199 | ```scala 200 | def foo(arg: String)(implicit line: sourcecode.Line, file: sourcecode.File) = { 201 | ... do something with arg ... 202 | ... do something with file.value ... 203 | } 204 | 205 | foo("hello") // the implicit sourcecode.File is filled in automatically 206 | ``` 207 | 208 | ## Maintenance status 209 | 210 | This library is community-maintained. It is not supported under the Lightbend subscription. 211 | 212 | ## Contribution policy 213 | 214 | Contributions via GitHub pull requests are gladly accepted from their original author. Before we can accept pull requests, you will need to agree to the [Lightbend Contributor License Agreement](https://www.lightbend.com/contribute/cla) online, using your GitHub account. 215 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | // basics 2 | 3 | name := "scala-logging" 4 | crossScalaVersions := Seq("3.3.5", "2.11.12", "2.12.20", "2.13.16") 5 | scalaVersion := crossScalaVersions.value.head 6 | ThisBuild / versionScheme := Some("early-semver") 7 | scalacOptions ++= Seq( 8 | "-unchecked", 9 | "-deprecation", 10 | "-language:_", 11 | "-encoding", "UTF-8", 12 | "-Ywarn-unused" 13 | ) 14 | incOptions := incOptions.value.withLogRecompileOnMacro(false) 15 | val isScala3 = Def.setting { 16 | CrossVersion.partialVersion(scalaVersion.value).exists(_._1 != 2) 17 | } 18 | libraryDependencies ++= Dependencies.scalaLogging(scalaVersion.value, isScala3.value) 19 | initialCommands := """|import com.typesafe.scalalogging._ 20 | |import org.slf4j.{ Logger => Underlying, _ }""".stripMargin 21 | 22 | // OSGi 23 | 24 | enablePlugins(SbtOsgi) 25 | osgiSettings 26 | OsgiKeys.bundleSymbolicName := "com.typesafe.scala-logging" 27 | OsgiKeys.privatePackage := Seq() 28 | OsgiKeys.exportPackage := Seq("com.typesafe.scalalogging*") 29 | 30 | // publishing 31 | 32 | organization := "com.typesafe.scala-logging" 33 | sonatypeProfileName := "com.typesafe" 34 | licenses := Seq("Apache 2.0 License" -> url("http://www.apache.org/licenses/LICENSE-2.0.html")) 35 | homepage := Some(url("https://github.com/lightbend/scala-logging")) 36 | Test / publishArtifact := false 37 | pomIncludeRepository := (_ => false) 38 | scmInfo := Some( 39 | ScmInfo(url("https://github.com/lightbend/scala-logging"), "scm:git:git@github.com:lightbend/scala-logging.git") 40 | ) 41 | developers := List( 42 | Developer( 43 | id = "hseeberger", 44 | name = "Heiko Seeberger", 45 | email = "", 46 | url = url("http://heikoseeberger.de") 47 | ), 48 | Developer( 49 | id = "analytically", 50 | name = "Mathias Bogaert", 51 | email = "", 52 | url = url("http://twitter.com/analytically") 53 | ) 54 | ) 55 | -------------------------------------------------------------------------------- /project/Dependencies.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | object Version { 4 | val logback = "1.2.12" 5 | val mockito = "3.2.10.0" 6 | val scalaTest = "3.2.19" 7 | val slf4j = "1.7.36" 8 | } 9 | 10 | object Library { 11 | val logbackClassic = "ch.qos.logback" % "logback-classic" % Version.logback 12 | val mockitoScala = "org.scalatestplus" %% "mockito-3-4" % Version.mockito 13 | def scalaReflect(scalaVersion: String) = "org.scala-lang" % "scala-reflect" % scalaVersion 14 | val scalaTest = "org.scalatest" %% "scalatest" % Version.scalaTest 15 | val slf4jApi = "org.slf4j" % "slf4j-api" % Version.slf4j 16 | } 17 | 18 | object Dependencies { 19 | import Library._ 20 | 21 | def scalaLogging(scalaVersion: String, isScala3: Boolean) = { 22 | List(scalaReflect(scalaVersion)).filter(_ => !isScala3) ++ 23 | List( 24 | slf4jApi, 25 | logbackClassic % "test", 26 | mockitoScala % "test", 27 | scalaTest % "test" 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.10.11 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") 2 | addSbtPlugin("com.github.sbt" % "sbt-osgi" % "0.10.0") 3 | addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.9.3") 4 | -------------------------------------------------------------------------------- /scalariform.sbt: -------------------------------------------------------------------------------- 1 | import com.typesafe.sbt.SbtScalariform.ScalariformKeys 2 | import scalariform.formatter.preferences.{AlignSingleLineCaseStatements, DoubleIndentConstructorArguments} 3 | 4 | ScalariformKeys.preferences := ScalariformKeys.preferences.value 5 | .setPreference(AlignSingleLineCaseStatements, true) 6 | .setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 100) 7 | .setPreference(DoubleIndentConstructorArguments, true) 8 | -------------------------------------------------------------------------------- /src/main/resources/LICENSE-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/main/scala-2/com/typesafe/scalalogging/LoggerImpl.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.Marker 4 | class LoggerImpl { 5 | 6 | // Error 7 | 8 | def error(message: String): Unit = macro LoggerMacro.errorMessage 9 | 10 | def error(message: String, cause: Throwable): Unit = macro LoggerMacro.errorMessageCause 11 | 12 | def error(message: String, args: Any*): Unit = macro LoggerMacro.errorMessageArgs 13 | 14 | def error(marker: Marker, message: String): Unit = macro LoggerMacro.errorMessageMarker 15 | 16 | def error(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro.errorMessageCauseMarker 17 | 18 | def error(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro.errorMessageArgsMarker 19 | 20 | def whenErrorEnabled(body: Unit): Unit = macro LoggerMacro.errorCode 21 | 22 | // Warn 23 | 24 | def warn(message: String): Unit = macro LoggerMacro.warnMessage 25 | 26 | def warn(message: String, cause: Throwable): Unit = macro LoggerMacro.warnMessageCause 27 | 28 | def warn(message: String, args: Any*): Unit = macro LoggerMacro.warnMessageArgs 29 | 30 | def warn(marker: Marker, message: String): Unit = macro LoggerMacro.warnMessageMarker 31 | 32 | def warn(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro.warnMessageCauseMarker 33 | 34 | def warn(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro.warnMessageArgsMarker 35 | 36 | def whenWarnEnabled(body: Unit): Unit = macro LoggerMacro.warnCode 37 | 38 | // Info 39 | 40 | def info(message: String): Unit = macro LoggerMacro.infoMessage 41 | 42 | def info(message: String, cause: Throwable): Unit = macro LoggerMacro.infoMessageCause 43 | 44 | def info(message: String, args: Any*): Unit = macro LoggerMacro.infoMessageArgs 45 | 46 | def info(marker: Marker, message: String): Unit = macro LoggerMacro.infoMessageMarker 47 | 48 | def info(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro.infoMessageCauseMarker 49 | 50 | def info(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro.infoMessageArgsMarker 51 | 52 | def whenInfoEnabled(body: Unit): Unit = macro LoggerMacro.infoCode 53 | 54 | // Debug 55 | 56 | def debug(message: String): Unit = macro LoggerMacro.debugMessage 57 | 58 | def debug(message: String, cause: Throwable): Unit = macro LoggerMacro.debugMessageCause 59 | 60 | def debug(message: String, args: Any*): Unit = macro LoggerMacro.debugMessageArgs 61 | 62 | def debug(marker: Marker, message: String): Unit = macro LoggerMacro.debugMessageMarker 63 | 64 | def debug(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro.debugMessageCauseMarker 65 | 66 | def debug(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro.debugMessageArgsMarker 67 | 68 | def whenDebugEnabled(body: Unit): Unit = macro LoggerMacro.debugCode 69 | 70 | // Trace 71 | 72 | def trace(message: String): Unit = macro LoggerMacro.traceMessage 73 | 74 | def trace(message: String, cause: Throwable): Unit = macro LoggerMacro.traceMessageCause 75 | 76 | def trace(message: String, args: Any*): Unit = macro LoggerMacro.traceMessageArgs 77 | 78 | def trace(marker: Marker, message: String): Unit = macro LoggerMacro.traceMessageMarker 79 | 80 | def trace(marker: Marker, message: String, cause: Throwable): Unit = macro LoggerMacro.traceMessageCauseMarker 81 | 82 | def trace(marker: Marker, message: String, args: Any*): Unit = macro LoggerMacro.traceMessageArgsMarker 83 | 84 | def whenTraceEnabled(body: Unit): Unit = macro LoggerMacro.traceCode 85 | } -------------------------------------------------------------------------------- /src/main/scala-2/com/typesafe/scalalogging/LoggerMacro.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.Marker 4 | import scala.reflect.macros.blackbox 5 | 6 | private[scalalogging] object LoggerMacro { 7 | 8 | type LoggerContext = blackbox.Context { type PrefixType = Logger } 9 | 10 | // Error 11 | 12 | def errorMessage(c: LoggerContext)(message: c.Expr[String]): c.universe.Tree = { 13 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 14 | errorMessageArgs(c)(messageFormat, args: _*) 15 | } 16 | 17 | def errorMessageCause(c: LoggerContext)(message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 18 | import c.universe._ 19 | val underlying = q"${c.prefix}.underlying" 20 | q"if ($underlying.isErrorEnabled) $underlying.error($message, $cause)" 21 | } 22 | 23 | def errorMessageArgs(c: LoggerContext)(message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 24 | import c.universe._ 25 | val underlying = q"${c.prefix}.underlying" 26 | val anyRefArgs = formatArgs(c)(args: _*) 27 | if (args.length == 2) 28 | q"if ($underlying.isErrorEnabled) $underlying.error($message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 29 | else 30 | q"if ($underlying.isErrorEnabled) $underlying.error($message, ..$anyRefArgs)" 31 | } 32 | 33 | def errorMessageMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String]): c.universe.Tree = { 34 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 35 | errorMessageArgsMarker(c)(marker, messageFormat, args: _*) 36 | } 37 | 38 | def errorMessageCauseMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 39 | import c.universe._ 40 | val underlying = q"${c.prefix}.underlying" 41 | q"if ($underlying.isErrorEnabled($marker)) $underlying.error($marker, $message, $cause)" 42 | } 43 | 44 | def errorMessageArgsMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 45 | import c.universe._ 46 | val underlying = q"${c.prefix}.underlying" 47 | val anyRefArgs = formatArgs(c)(args: _*) 48 | if (args.length == 2) 49 | q"if ($underlying.isErrorEnabled($marker)) $underlying.error($marker, $message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 50 | else 51 | q"if ($underlying.isErrorEnabled($marker)) $underlying.error($marker, $message, ..$anyRefArgs)" 52 | } 53 | 54 | def errorCode(c: LoggerContext)(body: c.Expr[Unit]): c.universe.Tree = { 55 | import c.universe._ 56 | val underlying = q"${c.prefix}.underlying" 57 | q"if ($underlying.isErrorEnabled) $body" 58 | } 59 | 60 | // Warn 61 | 62 | def warnMessage(c: LoggerContext)(message: c.Expr[String]): c.universe.Tree = { 63 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 64 | warnMessageArgs(c)(messageFormat, args: _*) 65 | } 66 | 67 | def warnMessageCause(c: LoggerContext)(message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 68 | import c.universe._ 69 | val underlying = q"${c.prefix}.underlying" 70 | q"if ($underlying.isWarnEnabled) $underlying.warn($message, $cause)" 71 | } 72 | 73 | def warnMessageArgs(c: LoggerContext)(message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 74 | import c.universe._ 75 | val underlying = q"${c.prefix}.underlying" 76 | val anyRefArgs = formatArgs(c)(args: _*) 77 | if (args.length == 2) 78 | q"if ($underlying.isWarnEnabled) $underlying.warn($message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 79 | else 80 | q"if ($underlying.isWarnEnabled) $underlying.warn($message, ..$anyRefArgs)" 81 | } 82 | 83 | def warnMessageMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String]): c.universe.Tree = { 84 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 85 | warnMessageArgsMarker(c)(marker, messageFormat, args: _*) 86 | } 87 | 88 | def warnMessageCauseMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 89 | import c.universe._ 90 | val underlying = q"${c.prefix}.underlying" 91 | q"if ($underlying.isWarnEnabled($marker)) $underlying.warn($marker, $message, $cause)" 92 | } 93 | 94 | def warnMessageArgsMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 95 | import c.universe._ 96 | val underlying = q"${c.prefix}.underlying" 97 | val anyRefArgs = formatArgs(c)(args: _*) 98 | if (args.length == 2) 99 | q"if ($underlying.isWarnEnabled($marker)) $underlying.warn($marker, $message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 100 | else 101 | q"if ($underlying.isWarnEnabled($marker)) $underlying.warn($marker, $message, ..$anyRefArgs)" 102 | } 103 | 104 | def warnCode(c: LoggerContext)(body: c.Expr[Unit]): c.universe.Tree = { 105 | import c.universe._ 106 | val underlying = q"${c.prefix}.underlying" 107 | q"if ($underlying.isWarnEnabled) $body" 108 | } 109 | 110 | // Info 111 | 112 | def infoMessage(c: LoggerContext)(message: c.Expr[String]): c.universe.Tree = { 113 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 114 | infoMessageArgs(c)(messageFormat, args: _*) 115 | } 116 | 117 | def infoMessageCause(c: LoggerContext)(message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 118 | import c.universe._ 119 | val underlying = q"${c.prefix}.underlying" 120 | q"if ($underlying.isInfoEnabled) $underlying.info($message, $cause)" 121 | } 122 | 123 | def infoMessageArgs(c: LoggerContext)(message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 124 | import c.universe._ 125 | val underlying = q"${c.prefix}.underlying" 126 | val anyRefArgs = formatArgs(c)(args: _*) 127 | if (args.length == 2) 128 | q"if ($underlying.isInfoEnabled) $underlying.info($message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 129 | else 130 | q"if ($underlying.isInfoEnabled) $underlying.info($message, ..$anyRefArgs)" 131 | } 132 | 133 | def infoMessageMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String]): c.universe.Tree = { 134 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 135 | infoMessageArgsMarker(c)(marker, messageFormat, args: _*) 136 | } 137 | 138 | def infoMessageCauseMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 139 | import c.universe._ 140 | val underlying = q"${c.prefix}.underlying" 141 | q"if ($underlying.isInfoEnabled($marker)) $underlying.info($marker, $message, $cause)" 142 | } 143 | 144 | def infoMessageArgsMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 145 | import c.universe._ 146 | val underlying = q"${c.prefix}.underlying" 147 | val anyRefArgs = formatArgs(c)(args: _*) 148 | if (args.length == 2) 149 | q"if ($underlying.isInfoEnabled($marker)) $underlying.info($marker, $message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 150 | else 151 | q"if ($underlying.isInfoEnabled($marker)) $underlying.info($marker, $message, ..$anyRefArgs)" 152 | } 153 | 154 | def infoCode(c: LoggerContext)(body: c.Expr[Unit]): c.universe.Tree = { 155 | import c.universe._ 156 | val underlying = q"${c.prefix}.underlying" 157 | q"if ($underlying.isInfoEnabled) $body" 158 | } 159 | 160 | // Debug 161 | 162 | def debugMessage(c: LoggerContext)(message: c.Expr[String]): c.universe.Tree = { 163 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 164 | debugMessageArgs(c)(messageFormat, args: _*) 165 | } 166 | 167 | def debugMessageCause(c: LoggerContext)(message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 168 | import c.universe._ 169 | val underlying = q"${c.prefix}.underlying" 170 | q"if ($underlying.isDebugEnabled) $underlying.debug($message, $cause)" 171 | } 172 | 173 | def debugMessageArgs(c: LoggerContext)(message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 174 | import c.universe._ 175 | val underlying = q"${c.prefix}.underlying" 176 | val anyRefArgs = formatArgs(c)(args: _*) 177 | if (args.length == 2) 178 | q"if ($underlying.isDebugEnabled) $underlying.debug($message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 179 | else 180 | q"if ($underlying.isDebugEnabled) $underlying.debug($message, ..$anyRefArgs)" 181 | } 182 | 183 | def debugMessageMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String]): c.universe.Tree = { 184 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 185 | debugMessageArgsMarker(c)(marker, messageFormat, args: _*) 186 | } 187 | 188 | def debugMessageCauseMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 189 | import c.universe._ 190 | val underlying = q"${c.prefix}.underlying" 191 | q"if ($underlying.isDebugEnabled($marker)) $underlying.debug($marker, $message, $cause)" 192 | } 193 | 194 | def debugMessageArgsMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 195 | import c.universe._ 196 | val underlying = q"${c.prefix}.underlying" 197 | val anyRefArgs = formatArgs(c)(args: _*) 198 | if (args.length == 2) 199 | q"if ($underlying.isDebugEnabled($marker)) $underlying.debug($marker, $message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 200 | else 201 | q"if ($underlying.isDebugEnabled($marker)) $underlying.debug($marker, $message, ..$anyRefArgs)" 202 | } 203 | 204 | def debugCode(c: LoggerContext)(body: c.Expr[Unit]): c.universe.Tree = { 205 | import c.universe._ 206 | val underlying = q"${c.prefix}.underlying" 207 | q"if ($underlying.isDebugEnabled) $body" 208 | } 209 | 210 | // Trace 211 | 212 | def traceMessage(c: LoggerContext)(message: c.Expr[String]): c.universe.Tree = { 213 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 214 | traceMessageArgs(c)(messageFormat, args: _*) 215 | } 216 | 217 | def traceMessageCause(c: LoggerContext)(message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 218 | import c.universe._ 219 | val underlying = q"${c.prefix}.underlying" 220 | q"if ($underlying.isTraceEnabled) $underlying.trace($message, $cause)" 221 | } 222 | 223 | def traceMessageArgs(c: LoggerContext)(message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 224 | import c.universe._ 225 | val underlying = q"${c.prefix}.underlying" 226 | val anyRefArgs = formatArgs(c)(args: _*) 227 | if (args.length == 2) 228 | q"if ($underlying.isTraceEnabled) $underlying.trace($message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 229 | else 230 | q"if ($underlying.isTraceEnabled) $underlying.trace($message, ..$anyRefArgs)" 231 | } 232 | 233 | def traceMessageMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String]): c.universe.Tree = { 234 | val (messageFormat, args) = deconstructInterpolatedMessage(c)(message) 235 | traceMessageArgsMarker(c)(marker, messageFormat, args: _*) 236 | } 237 | 238 | def traceMessageCauseMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable]): c.universe.Tree = { 239 | import c.universe._ 240 | val underlying = q"${c.prefix}.underlying" 241 | q"if ($underlying.isTraceEnabled($marker)) $underlying.trace($marker, $message, $cause)" 242 | } 243 | 244 | def traceMessageArgsMarker(c: LoggerContext)(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*): c.universe.Tree = { 245 | import c.universe._ 246 | val underlying = q"${c.prefix}.underlying" 247 | val anyRefArgs = formatArgs(c)(args: _*) 248 | if (args.length == 2) 249 | q"if ($underlying.isTraceEnabled($marker)) $underlying.trace($marker, $message, _root_.scala.Array[AnyRef](${anyRefArgs.head}, ${anyRefArgs(1)}): _*)" 250 | else 251 | q"if ($underlying.isTraceEnabled($marker)) $underlying.trace($marker, $message, ..$anyRefArgs)" 252 | } 253 | 254 | def traceCode(c: LoggerContext)(body: c.Expr[Unit]): c.universe.Tree = { 255 | import c.universe._ 256 | val underlying = q"${c.prefix}.underlying" 257 | q"if ($underlying.isTraceEnabled) $body" 258 | } 259 | 260 | /** Checks whether `message` is an interpolated string and transforms it into SLF4J string interpolation. */ 261 | private def deconstructInterpolatedMessage(c: LoggerContext)(message: c.Expr[String]) = { 262 | val u: c.universe.type = c.universe 263 | // Eww; gross! In 2.13, the `s` interpolator on StringContext became a macro, so we have to look at the pre-macro 264 | // expansion tree to recover what the user wrote... 265 | val tree: u.Tree = { 266 | // ... but there's no way to do that within scala.reflect.api! 267 | // Worse, MacroExpansionAttachment is in scala-compiler, not scala-reflect, so we don't even have it on the compilation classpath. 268 | // Hence, getClass.getSimplename.... 269 | val uInternal = u.asInstanceOf[scala.reflect.internal.SymbolTable] 270 | import uInternal._ 271 | message.tree.asInstanceOf[uInternal.Tree].attachments.all.collect { 272 | case orig if orig.getClass.getSimpleName == "MacroExpansionAttachment" => orig.asInstanceOf[{ def expandee: Tree }].expandee.asInstanceOf[u.Tree] 273 | }.headOption.getOrElse(message.tree) 274 | } 275 | 276 | import u._ 277 | 278 | tree match { 279 | case q"scala.StringContext.apply(..$parts).s(..$args)" => 280 | val format = parts.iterator.map({ case Literal(Constant(str: String)) => str }) 281 | // Emulate standard interpolator escaping 282 | .map(StringContext.processEscapes) 283 | // Escape literal slf4j format anchors if the resulting call will require a format string 284 | .map(str => if (args.nonEmpty) str.replace("{}", "\\{}") else str) 285 | .mkString("{}") 286 | 287 | val formatArgs = args.map(t => c.Expr[Any](t)) 288 | 289 | (c.Expr(q"$format"), formatArgs) 290 | 291 | case _ => (message, Seq.empty) 292 | } 293 | } 294 | 295 | private def formatArgs(c: LoggerContext)(args: c.Expr[Any]*) = { 296 | import c.universe._ 297 | args.map { arg => 298 | c.Expr[AnyRef](if (arg.tree.tpe <:< weakTypeOf[AnyRef]) q"$arg: _root_.scala.AnyRef" else q"$arg.asInstanceOf[_root_.scala.AnyRef]") 299 | } 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/main/scala-2/com/typesafe/scalalogging/LoggerTakingImplicitImpl.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.Marker 4 | 5 | class LoggerTakingImplicitImpl[A] private[scalalogging] { 6 | 7 | // Error 8 | 9 | def error(message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.errorMessage[A] 10 | 11 | def error(message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.errorMessageCause[A] 12 | 13 | def error(message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.errorMessageArgs[A] 14 | 15 | def error(marker: Marker, message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.errorMessageMarker[A] 16 | 17 | def error(marker: Marker, message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.errorMessageCauseMarker[A] 18 | 19 | def error(marker: Marker, message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.errorMessageArgsMarker[A] 20 | 21 | def whenErrorEnabled(body: Unit)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.errorCode[A] 22 | 23 | // Warn 24 | 25 | def warn(message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.warnMessage[A] 26 | 27 | def warn(message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.warnMessageCause[A] 28 | 29 | def warn(message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.warnMessageArgs[A] 30 | 31 | def warn(marker: Marker, message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.warnMessageMarker[A] 32 | 33 | def warn(marker: Marker, message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.warnMessageCauseMarker[A] 34 | 35 | def warn(marker: Marker, message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.warnMessageArgsMarker[A] 36 | 37 | def whenWarnEnabled(body: Unit)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.warnCode[A] 38 | 39 | // Info 40 | 41 | def info(message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.infoMessage[A] 42 | 43 | def info(message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.infoMessageCause[A] 44 | 45 | def info(message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.infoMessageArgs[A] 46 | 47 | def info(marker: Marker, message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.infoMessageMarker[A] 48 | 49 | def info(marker: Marker, message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.infoMessageCauseMarker[A] 50 | 51 | def info(marker: Marker, message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.infoMessageArgsMarker[A] 52 | 53 | def whenInfoEnabled(body: Unit)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.infoCode[A] 54 | 55 | // Debug 56 | 57 | def debug(message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.debugMessage[A] 58 | 59 | def debug(message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.debugMessageCause[A] 60 | 61 | def debug(message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.debugMessageArgs[A] 62 | 63 | def debug(marker: Marker, message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.debugMessageMarker[A] 64 | 65 | def debug(marker: Marker, message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.debugMessageCauseMarker[A] 66 | 67 | def debug(marker: Marker, message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.debugMessageArgsMarker[A] 68 | 69 | def whenDebugEnabled(body: Unit)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.debugCode[A] 70 | 71 | // Trace 72 | 73 | def trace(message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.traceMessage[A] 74 | 75 | def trace(message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.traceMessageCause[A] 76 | 77 | def trace(message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.traceMessageArgs[A] 78 | 79 | def trace(marker: Marker, message: String)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.traceMessageMarker[A] 80 | 81 | def trace(marker: Marker, message: String, cause: Throwable)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.traceMessageCauseMarker[A] 82 | 83 | def trace(marker: Marker, message: String, args: Any*)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.traceMessageArgsMarker[A] 84 | 85 | def whenTraceEnabled(body: Unit)(implicit a: A): Unit = macro LoggerTakingImplicitMacro.traceCode[A] 86 | } -------------------------------------------------------------------------------- /src/main/scala-2/com/typesafe/scalalogging/LoggerTakingImplicitMacro.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.Marker 4 | import scala.reflect.macros.blackbox 5 | 6 | private[scalalogging] object LoggerTakingImplicitMacro { 7 | 8 | type LoggerContext[A] = blackbox.Context { type PrefixType = LoggerTakingImplicit[A] } 9 | 10 | // Error 11 | 12 | def errorMessage[A](c: LoggerContext[A])(message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 13 | import c.universe._ 14 | val underlying = q"${c.prefix}.underlying" 15 | val canLogEv = q"${c.prefix}.canLogEv" 16 | q"""if ($underlying.isErrorEnabled) { 17 | $underlying.error($canLogEv.logMessage($message, $a)) 18 | $canLogEv.afterLog($a) 19 | }""" 20 | } 21 | 22 | def errorMessageCause[A](c: LoggerContext[A])(message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 23 | import c.universe._ 24 | val underlying = q"${c.prefix}.underlying" 25 | val canLogEv = q"${c.prefix}.canLogEv" 26 | q"""if ($underlying.isErrorEnabled) { 27 | $underlying.error($canLogEv.logMessage($message, $a), $cause) 28 | $canLogEv.afterLog($a) 29 | }""" 30 | } 31 | 32 | def errorMessageArgs[A](c: LoggerContext[A])(message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 33 | import c.universe._ 34 | val underlying = q"${c.prefix}.underlying" 35 | val canLogEv = q"${c.prefix}.canLogEv" 36 | if (args.length == 2) { 37 | q"""if ($underlying.isErrorEnabled) { 38 | $underlying.error($canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 39 | $canLogEv.afterLog($a) 40 | }""" 41 | } else { 42 | q"""if ($underlying.isErrorEnabled) { 43 | $underlying.error($canLogEv.logMessage($message, $a), ..$args) 44 | $canLogEv.afterLog($a) 45 | }""" 46 | } 47 | } 48 | 49 | def errorMessageMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 50 | import c.universe._ 51 | val underlying = q"${c.prefix}.underlying" 52 | val canLogEv = q"${c.prefix}.canLogEv" 53 | q"""if ($underlying.isErrorEnabled($marker)) { 54 | $underlying.error($marker, $canLogEv.logMessage($message, $a)) 55 | $canLogEv.afterLog($a) 56 | }""" 57 | } 58 | 59 | def errorMessageCauseMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 60 | import c.universe._ 61 | val underlying = q"${c.prefix}.underlying" 62 | val canLogEv = q"${c.prefix}.canLogEv" 63 | q"""if ($underlying.isErrorEnabled($marker)) { 64 | $underlying.error($marker, $canLogEv.logMessage($message, $a), $cause) 65 | $canLogEv.afterLog($a) 66 | }""" 67 | } 68 | 69 | def errorMessageArgsMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 70 | import c.universe._ 71 | val underlying = q"${c.prefix}.underlying" 72 | val canLogEv = q"${c.prefix}.canLogEv" 73 | if (args.length == 2) { 74 | q"""if ($underlying.isErrorEnabled($marker)) { 75 | $underlying.error($marker, $canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 76 | $canLogEv.afterLog($a) 77 | }""" 78 | } else { 79 | q"""if ($underlying.isErrorEnabled($marker)) { 80 | $underlying.error($marker, $canLogEv.logMessage($message, $a), ..$args) 81 | $canLogEv.afterLog($a) 82 | }""" 83 | } 84 | } 85 | 86 | def errorCode[A](c: LoggerContext[A])(body: c.Expr[Unit])(a: c.Expr[A]): c.universe.Tree = { 87 | import c.universe._ 88 | val underlying = q"${c.prefix}.underlying" 89 | val canLogEv = q"${c.prefix}.canLogEv" 90 | q"""if ($underlying.isErrorEnabled) { 91 | $underlying.error($canLogEv.logMessage($body)) 92 | $canLogEv.afterLog($a) 93 | }""" 94 | } 95 | 96 | // Warn 97 | 98 | def warnMessage[A](c: LoggerContext[A])(message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 99 | import c.universe._ 100 | val underlying = q"${c.prefix}.underlying" 101 | val canLogEv = q"${c.prefix}.canLogEv" 102 | q"""if ($underlying.isWarnEnabled) { 103 | $underlying.warn($canLogEv.logMessage($message, $a)) 104 | $canLogEv.afterLog($a) 105 | }""" 106 | } 107 | 108 | def warnMessageCause[A](c: LoggerContext[A])(message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 109 | import c.universe._ 110 | val underlying = q"${c.prefix}.underlying" 111 | val canLogEv = q"${c.prefix}.canLogEv" 112 | q"""if ($underlying.isWarnEnabled) { 113 | $underlying.warn($canLogEv.logMessage($message, $a), $cause) 114 | $canLogEv.afterLog($a) 115 | }""" 116 | } 117 | 118 | def warnMessageArgs[A](c: LoggerContext[A])(message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 119 | import c.universe._ 120 | val underlying = q"${c.prefix}.underlying" 121 | val canLogEv = q"${c.prefix}.canLogEv" 122 | if (args.length == 2) { 123 | q"""if ($underlying.isWarnEnabled) { 124 | $underlying.warn($canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 125 | $canLogEv.afterLog($a) 126 | }""" 127 | } else { 128 | q"""if ($underlying.isWarnEnabled) { 129 | $underlying.warn($canLogEv.logMessage($message, $a), ..$args) 130 | $canLogEv.afterLog($a) 131 | }""" 132 | } 133 | } 134 | 135 | def warnMessageMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 136 | import c.universe._ 137 | val underlying = q"${c.prefix}.underlying" 138 | val canLogEv = q"${c.prefix}.canLogEv" 139 | q"""if ($underlying.isWarnEnabled($marker)) { 140 | $underlying.warn($marker, $canLogEv.logMessage($message, $a)) 141 | $canLogEv.afterLog($a) 142 | }""" 143 | } 144 | 145 | def warnMessageCauseMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 146 | import c.universe._ 147 | val underlying = q"${c.prefix}.underlying" 148 | val canLogEv = q"${c.prefix}.canLogEv" 149 | q"""if ($underlying.isWarnEnabled($marker)) { 150 | $underlying.warn($marker, $canLogEv.logMessage($message, $a), $cause) 151 | $canLogEv.afterLog($a) 152 | }""" 153 | } 154 | 155 | def warnMessageArgsMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 156 | import c.universe._ 157 | val underlying = q"${c.prefix}.underlying" 158 | val canLogEv = q"${c.prefix}.canLogEv" 159 | if (args.length == 2) { 160 | q"""if ($underlying.isWarnEnabled($marker)) { 161 | $underlying.warn($marker, $canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 162 | $canLogEv.afterLog($a) 163 | }""" 164 | } else { 165 | q"""if ($underlying.isWarnEnabled($marker)) { 166 | $underlying.warn($marker, $canLogEv.logMessage($message, $a), ..$args) 167 | $canLogEv.afterLog($a) 168 | }""" 169 | } 170 | } 171 | 172 | def warnCode[A](c: LoggerContext[A])(body: c.Expr[Unit])(a: c.Expr[A]): c.universe.Tree = { 173 | import c.universe._ 174 | val underlying = q"${c.prefix}.underlying" 175 | val canLogEv = q"${c.prefix}.canLogEv" 176 | q"""if ($underlying.isWarnEnabled) { 177 | $underlying.warn($canLogEv.logMessage($body)) 178 | $canLogEv.afterLog($a) 179 | }""" 180 | } 181 | 182 | // Info 183 | 184 | def infoMessage[A](c: LoggerContext[A])(message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 185 | import c.universe._ 186 | val underlying = q"${c.prefix}.underlying" 187 | val canLogEv = q"${c.prefix}.canLogEv" 188 | q"""if ($underlying.isInfoEnabled) { 189 | $underlying.info($canLogEv.logMessage($message, $a)) 190 | $canLogEv.afterLog($a) 191 | }""" 192 | } 193 | 194 | def infoMessageCause[A](c: LoggerContext[A])(message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 195 | import c.universe._ 196 | val underlying = q"${c.prefix}.underlying" 197 | val canLogEv = q"${c.prefix}.canLogEv" 198 | q"""if ($underlying.isInfoEnabled) { 199 | $underlying.info($canLogEv.logMessage($message, $a), $cause) 200 | $canLogEv.afterLog($a) 201 | }""" 202 | } 203 | 204 | def infoMessageArgs[A](c: LoggerContext[A])(message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 205 | import c.universe._ 206 | val underlying = q"${c.prefix}.underlying" 207 | val canLogEv = q"${c.prefix}.canLogEv" 208 | if (args.length == 2) { 209 | q"""if ($underlying.isInfoEnabled) { 210 | $underlying.info($canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 211 | $canLogEv.afterLog($a) 212 | }""" 213 | } else { 214 | q"""if ($underlying.isInfoEnabled) { 215 | $underlying.info($canLogEv.logMessage($message, $a), ..$args) 216 | $canLogEv.afterLog($a) 217 | }""" 218 | } 219 | } 220 | 221 | def infoMessageMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 222 | import c.universe._ 223 | val underlying = q"${c.prefix}.underlying" 224 | val canLogEv = q"${c.prefix}.canLogEv" 225 | q"""if ($underlying.isInfoEnabled($marker)) { 226 | $underlying.info($marker, $canLogEv.logMessage($message, $a)) 227 | $canLogEv.afterLog($a) 228 | }""" 229 | } 230 | 231 | def infoMessageCauseMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 232 | import c.universe._ 233 | val underlying = q"${c.prefix}.underlying" 234 | val canLogEv = q"${c.prefix}.canLogEv" 235 | q"""if ($underlying.isInfoEnabled($marker)) { 236 | $underlying.info($marker, $canLogEv.logMessage($message, $a), $cause) 237 | $canLogEv.afterLog($a) 238 | }""" 239 | } 240 | 241 | def infoMessageArgsMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 242 | import c.universe._ 243 | val underlying = q"${c.prefix}.underlying" 244 | val canLogEv = q"${c.prefix}.canLogEv" 245 | if (args.length == 2) { 246 | q"""if ($underlying.isInfoEnabled($marker)) { 247 | $underlying.info($marker, $canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 248 | $canLogEv.afterLog($a) 249 | }""" 250 | } else { 251 | q"""if ($underlying.isInfoEnabled($marker)) { 252 | $underlying.info($marker, $canLogEv.logMessage($message, $a), ..$args) 253 | $canLogEv.afterLog($a) 254 | }""" 255 | } 256 | } 257 | 258 | def infoCode[A](c: LoggerContext[A])(body: c.Expr[Unit])(a: c.Expr[A]): c.universe.Tree = { 259 | import c.universe._ 260 | val underlying = q"${c.prefix}.underlying" 261 | val canLogEv = q"${c.prefix}.canLogEv" 262 | q"""if ($underlying.isInfoEnabled) { 263 | $underlying.info($canLogEv.logMessage($body)) 264 | $canLogEv.afterLog($a) 265 | }""" 266 | } 267 | 268 | // Debug 269 | 270 | def debugMessage[A](c: LoggerContext[A])(message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 271 | import c.universe._ 272 | val underlying = q"${c.prefix}.underlying" 273 | val canLogEv = q"${c.prefix}.canLogEv" 274 | q"""if ($underlying.isDebugEnabled) { 275 | $underlying.debug($canLogEv.logMessage($message, $a)) 276 | $canLogEv.afterLog($a) 277 | }""" 278 | } 279 | 280 | def debugMessageCause[A](c: LoggerContext[A])(message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 281 | import c.universe._ 282 | val underlying = q"${c.prefix}.underlying" 283 | val canLogEv = q"${c.prefix}.canLogEv" 284 | q"""if ($underlying.isDebugEnabled) { 285 | $underlying.debug($canLogEv.logMessage($message, $a), $cause) 286 | $canLogEv.afterLog($a) 287 | }""" 288 | } 289 | 290 | def debugMessageArgs[A](c: LoggerContext[A])(message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 291 | import c.universe._ 292 | val underlying = q"${c.prefix}.underlying" 293 | val canLogEv = q"${c.prefix}.canLogEv" 294 | if (args.length == 2) { 295 | q"""if ($underlying.isDebugEnabled) { 296 | $underlying.debug($canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 297 | $canLogEv.afterLog($a) 298 | }""" 299 | } else { 300 | q"""if ($underlying.isDebugEnabled) { 301 | $underlying.debug($canLogEv.logMessage($message, $a), ..$args) 302 | $canLogEv.afterLog($a) 303 | }""" 304 | } 305 | } 306 | 307 | def debugMessageMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 308 | import c.universe._ 309 | val underlying = q"${c.prefix}.underlying" 310 | val canLogEv = q"${c.prefix}.canLogEv" 311 | q"""if ($underlying.isDebugEnabled($marker)) { 312 | $underlying.debug($marker, $canLogEv.logMessage($message, $a)) 313 | $canLogEv.afterLog($a) 314 | }""" 315 | } 316 | 317 | def debugMessageCauseMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 318 | import c.universe._ 319 | val underlying = q"${c.prefix}.underlying" 320 | val canLogEv = q"${c.prefix}.canLogEv" 321 | q"""if ($underlying.isDebugEnabled($marker)) { 322 | $underlying.debug($marker, $canLogEv.logMessage($message, $a), $cause) 323 | $canLogEv.afterLog($a) 324 | }""" 325 | } 326 | 327 | def debugMessageArgsMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 328 | import c.universe._ 329 | val underlying = q"${c.prefix}.underlying" 330 | val canLogEv = q"${c.prefix}.canLogEv" 331 | if (args.length == 2) { 332 | q"""if ($underlying.isDebugEnabled($marker)) { 333 | $underlying.debug($marker, $canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 334 | $canLogEv.afterLog($a) 335 | }""" 336 | } else { 337 | q"""if ($underlying.isDebugEnabled($marker)) { 338 | $underlying.debug($marker, $canLogEv.logMessage($message, $a), ..$args) 339 | $canLogEv.afterLog($a) 340 | }""" 341 | } 342 | } 343 | 344 | def debugCode[A](c: LoggerContext[A])(body: c.Expr[Unit])(a: c.Expr[A]): c.universe.Tree = { 345 | import c.universe._ 346 | val underlying = q"${c.prefix}.underlying" 347 | val canLogEv = q"${c.prefix}.canLogEv" 348 | q"""if ($underlying.isDebugEnabled) { 349 | $underlying.debug($canLogEv.logMessage($body)) 350 | $canLogEv.afterLog($a) 351 | }""" 352 | } 353 | 354 | // Trace 355 | 356 | def traceMessage[A](c: LoggerContext[A])(message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 357 | import c.universe._ 358 | val underlying = q"${c.prefix}.underlying" 359 | val canLogEv = q"${c.prefix}.canLogEv" 360 | q"""if ($underlying.isTraceEnabled) { 361 | $underlying.trace($canLogEv.logMessage($message, $a)) 362 | $canLogEv.afterLog($a) 363 | }""" 364 | } 365 | 366 | def traceMessageCause[A](c: LoggerContext[A])(message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 367 | import c.universe._ 368 | val underlying = q"${c.prefix}.underlying" 369 | val canLogEv = q"${c.prefix}.canLogEv" 370 | q"""if ($underlying.isTraceEnabled) { 371 | $underlying.trace($canLogEv.logMessage($message, $a), $cause) 372 | $canLogEv.afterLog($a) 373 | }""" 374 | } 375 | 376 | def traceMessageArgs[A](c: LoggerContext[A])(message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 377 | import c.universe._ 378 | val underlying = q"${c.prefix}.underlying" 379 | val canLogEv = q"${c.prefix}.canLogEv" 380 | if (args.length == 2) { 381 | q"""if ($underlying.isTraceEnabled) { 382 | $underlying.trace($canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 383 | $canLogEv.afterLog($a) 384 | }""" 385 | } else { 386 | q"""if ($underlying.isTraceEnabled) { 387 | $underlying.trace($canLogEv.logMessage($message, $a), ..$args) 388 | $canLogEv.afterLog($a) 389 | }""" 390 | } 391 | } 392 | 393 | def traceMessageMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String])(a: c.Expr[A]): c.universe.Tree = { 394 | import c.universe._ 395 | val underlying = q"${c.prefix}.underlying" 396 | val canLogEv = q"${c.prefix}.canLogEv" 397 | q"""if ($underlying.isTraceEnabled($marker)) { 398 | $underlying.trace($marker, $canLogEv.logMessage($message, $a)) 399 | $canLogEv.afterLog($a) 400 | }""" 401 | } 402 | 403 | def traceMessageCauseMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], cause: c.Expr[Throwable])(a: c.Expr[A]): c.universe.Tree = { 404 | import c.universe._ 405 | val underlying = q"${c.prefix}.underlying" 406 | val canLogEv = q"${c.prefix}.canLogEv" 407 | q"""if ($underlying.isTraceEnabled($marker)) { 408 | $underlying.trace($marker, $canLogEv.logMessage($message, $a), $cause) 409 | $canLogEv.afterLog($a) 410 | }""" 411 | } 412 | 413 | def traceMessageArgsMarker[A](c: LoggerContext[A])(marker: c.Expr[Marker], message: c.Expr[String], args: c.Expr[Any]*)(a: c.Expr[A]): c.universe.Tree = { 414 | import c.universe._ 415 | val underlying = q"${c.prefix}.underlying" 416 | val canLogEv = q"${c.prefix}.canLogEv" 417 | if (args.length == 2) { 418 | q"""if ($underlying.isTraceEnabled($marker)) { 419 | $underlying.trace($marker, $canLogEv.logMessage($message, $a), List(${args(0)}, ${args(1)}): _*) 420 | $canLogEv.afterLog($a) 421 | }""" 422 | } else { 423 | q"""if ($underlying.isTraceEnabled($marker)) { 424 | $underlying.trace($marker, $canLogEv.logMessage($message, $a), ..$args) 425 | $canLogEv.afterLog($a) 426 | }""" 427 | } 428 | } 429 | 430 | def traceCode[A](c: LoggerContext[A])(body: c.Expr[Unit])(a: c.Expr[A]): c.universe.Tree = { 431 | import c.universe._ 432 | val underlying = q"${c.prefix}.underlying" 433 | val canLogEv = q"${c.prefix}.canLogEv" 434 | q"""if ($underlying.isTraceEnabled) { 435 | $underlying.trace($canLogEv.logMessage($body)) 436 | $canLogEv.afterLog($a) 437 | }""" 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /src/main/scala-3/com/typesafe/scalalogging/LoggerImpl.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.{Marker, Logger as Underlying } 4 | 5 | trait LoggerImpl { 6 | def underlying: Underlying 7 | 8 | 9 | // Error 10 | inline def error(inline message: String): Unit = ${LoggerMacro.errorMessage('underlying, 'message)} 11 | inline def error(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.errorMessageCause('underlying, 'message, 'cause)} 12 | inline def error(inline message: String, inline args: Any*): Unit = ${LoggerMacro.errorMessageArgs('underlying, 'message, 'args)} 13 | inline def error(inline marker: Marker, inline message: String): Unit = ${LoggerMacro.errorMessageMarker('underlying, 'marker, 'message)} 14 | inline def error(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.errorMessageCauseMarker('underlying, 'marker, 'message, 'cause)} 15 | inline def error(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro.errorMessageArgsMarker('underlying, 'marker, 'message, 'args)} 16 | inline def whenErrorEnabled(inline body: Unit): Unit = ${LoggerMacro.errorCode('underlying, 'body)} 17 | 18 | 19 | // Warn 20 | inline def warn(inline message: String): Unit = ${LoggerMacro.warnMessage('underlying, 'message)} 21 | inline def warn(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.warnMessageCause('underlying, 'message, 'cause)} 22 | inline def warn(inline message: String, inline args: Any*): Unit = ${LoggerMacro.warnMessageArgs('underlying, 'message, 'args)} 23 | inline def warn(inline marker: Marker, inline message: String): Unit = ${LoggerMacro.warnMessageMarker('underlying, 'marker, 'message)} 24 | inline def warn(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.warnMessageCauseMarker('underlying, 'marker, 'message, 'cause)} 25 | inline def warn(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro.warnMessageArgsMarker('underlying, 'marker, 'message, 'args)} 26 | inline def whenWarnEnabled(inline body: Unit): Unit = ${LoggerMacro.warnCode('underlying, 'body)} 27 | 28 | 29 | // Info 30 | inline def info(inline message: String): Unit = ${LoggerMacro.infoMessage('underlying, 'message)} 31 | inline def info(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.infoMessageCause('underlying, 'message, 'cause)} 32 | inline def info(inline message: String, inline args: Any*): Unit = ${LoggerMacro.infoMessageArgs('underlying, 'message, 'args)} 33 | inline def info(inline marker: Marker, inline message: String): Unit = ${LoggerMacro.infoMessageMarker('underlying, 'marker, 'message)} 34 | inline def info(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.infoMessageCauseMarker('underlying, 'marker, 'message, 'cause)} 35 | inline def info(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro.infoMessageArgsMarker('underlying, 'marker, 'message, 'args)} 36 | inline def whenInfoEnabled(inline body: Unit): Unit = ${LoggerMacro.infoCode('underlying, 'body)} 37 | 38 | 39 | // Debug 40 | inline def debug(inline message: String): Unit = ${LoggerMacro.debugMessage('underlying, 'message)} 41 | inline def debug(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.debugMessageCause('underlying, 'message, 'cause)} 42 | inline def debug(inline message: String, inline args: Any*): Unit = ${LoggerMacro.debugMessageArgs('underlying, 'message, 'args)} 43 | inline def debug(inline marker: Marker, inline message: String): Unit = ${LoggerMacro.debugMessageMarker('underlying, 'marker, 'message)} 44 | inline def debug(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.debugMessageCauseMarker('underlying, 'marker, 'message, 'cause)} 45 | inline def debug(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro.debugMessageArgsMarker('underlying, 'marker, 'message, 'args)} 46 | inline def whenDebugEnabled(inline body: Unit): Unit = ${LoggerMacro.debugCode('underlying, 'body)} 47 | 48 | 49 | // Trace 50 | inline def trace(inline message: String): Unit = ${LoggerMacro.traceMessage('underlying, 'message)} 51 | inline def trace(inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.traceMessageCause('underlying, 'message, 'cause)} 52 | inline def trace(inline message: String, inline args: Any*): Unit = ${LoggerMacro.traceMessageArgs('underlying, 'message, 'args)} 53 | inline def trace(inline marker: Marker, inline message: String): Unit = ${LoggerMacro.traceMessageMarker('underlying, 'marker, 'message)} 54 | inline def trace(inline marker: Marker, inline message: String, inline cause: Throwable): Unit = ${LoggerMacro.traceMessageCauseMarker('underlying, 'marker, 'message, 'cause)} 55 | inline def trace(inline marker: Marker, inline message: String, inline args: Any*): Unit = ${LoggerMacro.traceMessageArgsMarker('underlying, 'marker, 'message, 'args)} 56 | inline def whenTraceEnabled(inline body: Unit): Unit = ${LoggerMacro.traceCode('underlying, 'body)} 57 | } 58 | -------------------------------------------------------------------------------- /src/main/scala-3/com/typesafe/scalalogging/LoggerMacro.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.Marker 4 | import scala.quoted.* 5 | import org.slf4j.{ Logger as Underlying } 6 | 7 | private[scalalogging] object LoggerMacro { 8 | 9 | // Error 10 | 11 | def errorMessage(underlying: Expr[Underlying], message: Expr[String])(using Quotes): Expr[Unit] = { 12 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 13 | errorMessageArgs(underlying, messageFormat, Expr.ofSeq(args)) 14 | } 15 | 16 | def errorMessageCause(underlying: Expr[Underlying], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 17 | '{ if ($underlying.isErrorEnabled) $underlying.error($message, $cause) } 18 | 19 | def errorMessageArgs(underlying: Expr[Underlying], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 20 | val anyRefArgs = formatArgs(args) 21 | if(anyRefArgs.isEmpty) 22 | '{ if ($underlying.isErrorEnabled) $underlying.error($message) } 23 | else if(anyRefArgs.lengthIs == 1) 24 | '{ if ($underlying.isErrorEnabled) $underlying.error($message, ${anyRefArgs.head}) } 25 | else 26 | '{ if ($underlying.isErrorEnabled) $underlying.error($message, ${Expr.ofSeq(anyRefArgs)}*) } 27 | } 28 | 29 | def errorMessageMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String]) (using Quotes) = { 30 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 31 | errorMessageArgsMarker(underlying, marker, messageFormat, Expr.ofSeq(args)) 32 | } 33 | 34 | def errorMessageCauseMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 35 | '{ if ($underlying.isErrorEnabled($marker)) $underlying.error($marker, $message, $cause) } 36 | 37 | def errorMessageArgsMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 38 | val anyRefArgs = formatArgs(args) 39 | if(anyRefArgs.isEmpty) 40 | '{ if ($underlying.isErrorEnabled($marker)) $underlying.error($marker, $message) } 41 | else if(anyRefArgs.lengthIs == 1) 42 | '{if ($underlying.isErrorEnabled($marker)) $underlying.error($marker, $message, ${anyRefArgs.head}) } 43 | else 44 | '{ if ($underlying.isErrorEnabled($marker)) $underlying.error($marker, $message, ${Expr.ofSeq(anyRefArgs)}*) } 45 | } 46 | 47 | def errorCode(underlying: Expr[Underlying], body: Expr[Unit]) (using Quotes) = 48 | '{ if ($underlying.isErrorEnabled) $body } 49 | 50 | 51 | 52 | 53 | // Warn 54 | 55 | def warnMessage(underlying: Expr[Underlying], message: Expr[String])(using Quotes): Expr[Unit] = { 56 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 57 | warnMessageArgs(underlying, messageFormat, Expr.ofSeq(args)) 58 | } 59 | 60 | def warnMessageCause(underlying: Expr[Underlying], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 61 | '{ if ($underlying.isWarnEnabled) $underlying.warn($message, $cause) } 62 | 63 | def warnMessageArgs(underlying: Expr[Underlying], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 64 | val anyRefArgs = formatArgs(args) 65 | if(anyRefArgs.isEmpty) 66 | '{ if ($underlying.isWarnEnabled) $underlying.warn($message) } 67 | else if(anyRefArgs.lengthIs == 1) 68 | '{ if ($underlying.isWarnEnabled) $underlying.warn($message, ${anyRefArgs.head}) } 69 | else 70 | '{ if ($underlying.isWarnEnabled) $underlying.warn($message, ${Expr.ofSeq(anyRefArgs)}*) } 71 | } 72 | 73 | def warnMessageMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String]) (using Quotes) = { 74 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 75 | warnMessageArgsMarker(underlying, marker, messageFormat, Expr.ofSeq(args)) 76 | } 77 | 78 | def warnMessageCauseMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 79 | '{ if ($underlying.isWarnEnabled($marker)) $underlying.warn($marker, $message, $cause) } 80 | 81 | def warnMessageArgsMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 82 | val anyRefArgs = formatArgs(args) 83 | if(anyRefArgs.isEmpty) 84 | '{ if ($underlying.isWarnEnabled($marker)) $underlying.warn($marker, $message) } 85 | else if(anyRefArgs.lengthIs == 1) 86 | '{if ($underlying.isWarnEnabled($marker)) $underlying.warn($marker, $message, ${anyRefArgs.head}) } 87 | else 88 | '{ if ($underlying.isWarnEnabled($marker)) $underlying.warn($marker, $message, ${Expr.ofSeq(anyRefArgs)}*) } 89 | } 90 | 91 | def warnCode(underlying: Expr[Underlying], body: Expr[Unit]) (using Quotes) = 92 | '{ if ($underlying.isWarnEnabled) $body } 93 | 94 | 95 | 96 | 97 | // Info 98 | 99 | def infoMessage(underlying: Expr[Underlying], message: Expr[String])(using Quotes): Expr[Unit] = { 100 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 101 | infoMessageArgs(underlying, messageFormat, Expr.ofSeq(args)) 102 | } 103 | 104 | def infoMessageCause(underlying: Expr[Underlying], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 105 | '{ if ($underlying.isInfoEnabled) $underlying.info($message, $cause) } 106 | 107 | def infoMessageArgs(underlying: Expr[Underlying], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 108 | val anyRefArgs = formatArgs(args) 109 | if(anyRefArgs.isEmpty) 110 | '{ if ($underlying.isInfoEnabled) $underlying.info($message) } 111 | else if(anyRefArgs.lengthIs == 1) 112 | '{ if ($underlying.isInfoEnabled) $underlying.info($message, ${anyRefArgs.head}) } 113 | else 114 | '{ if ($underlying.isInfoEnabled) $underlying.info($message, ${Expr.ofSeq(anyRefArgs)}*) } 115 | } 116 | 117 | def infoMessageMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String]) (using Quotes) = { 118 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 119 | infoMessageArgsMarker(underlying, marker, messageFormat, Expr.ofSeq(args)) 120 | } 121 | 122 | def infoMessageCauseMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 123 | '{ if ($underlying.isInfoEnabled($marker)) $underlying.info($marker, $message, $cause) } 124 | 125 | def infoMessageArgsMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 126 | val anyRefArgs = formatArgs(args) 127 | if(anyRefArgs.isEmpty) 128 | '{ if ($underlying.isInfoEnabled($marker)) $underlying.info($marker, $message) } 129 | else if(anyRefArgs.lengthIs == 1) 130 | '{if ($underlying.isInfoEnabled($marker)) $underlying.info($marker, $message, ${anyRefArgs.head}) } 131 | else 132 | '{ if ($underlying.isInfoEnabled($marker)) $underlying.info($marker, $message, ${Expr.ofSeq(anyRefArgs)}*) } 133 | } 134 | 135 | def infoCode(underlying: Expr[Underlying], body: Expr[Unit]) (using Quotes) = 136 | '{ if ($underlying.isInfoEnabled) $body } 137 | 138 | 139 | 140 | 141 | 142 | // Debug 143 | 144 | def debugMessage(underlying: Expr[Underlying], message: Expr[String])(using Quotes): Expr[Unit] = { 145 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 146 | debugMessageArgs(underlying, messageFormat, Expr.ofSeq(args)) 147 | } 148 | 149 | def debugMessageCause(underlying: Expr[Underlying], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 150 | '{ if ($underlying.isDebugEnabled) $underlying.debug($message, $cause) } 151 | 152 | def debugMessageArgs(underlying: Expr[Underlying], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 153 | val anyRefArgs = formatArgs(args) 154 | if(anyRefArgs.isEmpty) 155 | '{ if ($underlying.isDebugEnabled) $underlying.debug($message) } 156 | else if(anyRefArgs.lengthIs == 1) 157 | '{ if ($underlying.isDebugEnabled) $underlying.debug($message, ${anyRefArgs.head}) } 158 | else 159 | '{ if ($underlying.isDebugEnabled) $underlying.debug($message, ${Expr.ofSeq(anyRefArgs)}*) } 160 | } 161 | 162 | def debugMessageMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String]) (using Quotes) = { 163 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 164 | debugMessageArgsMarker(underlying, marker, messageFormat, Expr.ofSeq(args)) 165 | } 166 | 167 | def debugMessageCauseMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 168 | '{ if ($underlying.isDebugEnabled($marker)) $underlying.debug($marker, $message, $cause) } 169 | 170 | def debugMessageArgsMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 171 | val anyRefArgs = formatArgs(args) 172 | if(anyRefArgs.isEmpty) 173 | '{ if ($underlying.isDebugEnabled($marker)) $underlying.debug($marker, $message) } 174 | else if(anyRefArgs.lengthIs == 1) 175 | '{if ($underlying.isDebugEnabled($marker)) $underlying.debug($marker, $message, ${anyRefArgs.head}) } 176 | else 177 | '{ if ($underlying.isDebugEnabled($marker)) $underlying.debug($marker, $message, ${Expr.ofSeq(anyRefArgs)}*) } 178 | } 179 | 180 | def debugCode(underlying: Expr[Underlying], body: Expr[Unit]) (using Quotes) = 181 | '{ if ($underlying.isDebugEnabled) $body } 182 | 183 | 184 | 185 | 186 | // Trace 187 | 188 | def traceMessage(underlying: Expr[Underlying], message: Expr[String])(using Quotes): Expr[Unit] = { 189 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 190 | traceMessageArgs(underlying, messageFormat, Expr.ofSeq(args)) 191 | } 192 | 193 | def traceMessageCause(underlying: Expr[Underlying], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 194 | '{ if ($underlying.isTraceEnabled) $underlying.trace($message, $cause) } 195 | 196 | def traceMessageArgs(underlying: Expr[Underlying], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 197 | val anyRefArgs = formatArgs(args) 198 | if(anyRefArgs.isEmpty) 199 | '{ if ($underlying.isTraceEnabled) $underlying.trace($message) } 200 | else if(anyRefArgs.lengthIs == 1) 201 | '{ if ($underlying.isTraceEnabled) $underlying.trace($message, ${anyRefArgs.head}) } 202 | else 203 | '{ if ($underlying.isTraceEnabled) $underlying.trace($message, ${Expr.ofSeq(anyRefArgs)}*) } 204 | } 205 | 206 | def traceMessageMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String]) (using Quotes) = { 207 | val (messageFormat, args) = deconstructInterpolatedMessage(message) 208 | traceMessageArgsMarker(underlying, marker, messageFormat, Expr.ofSeq(args)) 209 | } 210 | 211 | def traceMessageCauseMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable]) (using Quotes) = 212 | '{ if ($underlying.isTraceEnabled($marker)) $underlying.trace($marker, $message, $cause) } 213 | 214 | def traceMessageArgsMarker(underlying: Expr[Underlying], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]]) (using Quotes) = { 215 | val anyRefArgs = formatArgs(args) 216 | if(anyRefArgs.isEmpty) 217 | '{ if ($underlying.isTraceEnabled($marker)) $underlying.trace($marker, $message) } 218 | else if(anyRefArgs.lengthIs == 1) 219 | '{if ($underlying.isTraceEnabled($marker)) $underlying.trace($marker, $message, ${anyRefArgs.head}) } 220 | else 221 | '{ if ($underlying.isTraceEnabled($marker)) $underlying.trace($marker, $message, ${Expr.ofSeq(anyRefArgs)}*) } 222 | } 223 | 224 | def traceCode(underlying: Expr[Underlying], body: Expr[Unit]) (using Quotes) = 225 | '{ if ($underlying.isTraceEnabled) $body } 226 | 227 | 228 | 229 | /** Checks whether `message` is an interpolated string and transforms it into SLF4J string interpolation. */ 230 | private def deconstructInterpolatedMessage(message: Expr[String])(using Quotes): (Expr[String], Seq[Expr[Any]]) = { 231 | import quotes.reflect.* 232 | import util.* 233 | 234 | message.asTerm match{ 235 | case Inlined(_, _, Apply(Select(Apply(Select(Select(_, "StringContext"), _), messageNode), _), argumentsNode)) => 236 | val messageTextPartsOpt: Option[List[String]] = 237 | messageNode.collectFirst{ 238 | case Typed(Repeated(ls, _), _) => 239 | ls.collect{ case Literal(StringConstant(s)) => s} 240 | } 241 | val argsOpt: Option[List[Term]] = 242 | argumentsNode.collectFirst { 243 | case Typed(Repeated(ls, _), _) => ls 244 | } 245 | 246 | (messageTextPartsOpt, argsOpt) match{ 247 | case (Some(messageTextParts), Some(args)) => 248 | val format = messageTextParts.iterator 249 | // Emulate standard interpolator escaping 250 | .map(StringContext.processEscapes) 251 | // Escape literal slf4j format anchors if the resulting call will require a format string 252 | .map(str => if (args.nonEmpty) str.replace("{}", "\\{}") else str) 253 | .mkString("{}") 254 | 255 | val formatArgs = args.map(_.asExpr) 256 | 257 | (Expr(format), formatArgs) 258 | case _ => 259 | (message, Seq.empty) 260 | } 261 | case _ => (message, Seq.empty) 262 | } 263 | } 264 | def formatArgs(args: Expr[Seq[Any]])(using q: Quotes): Seq[Expr[AnyRef]] = { 265 | import quotes.reflect.* 266 | import util.* 267 | 268 | args.asTerm match { 269 | case p@Inlined(_, _, Typed(Repeated(v, _),_)) => 270 | v.map{ 271 | case t if t.tpe <:< TypeRepr.of[AnyRef] => t.asExprOf[AnyRef] 272 | case t => '{${t.asExpr}.asInstanceOf[AnyRef]} 273 | } 274 | case Repeated(v, _) => 275 | v.map{ 276 | case t if t.tpe <:< TypeRepr.of[AnyRef] => t.asExprOf[AnyRef] 277 | case t => '{${t.asExpr}.asInstanceOf[AnyRef]} 278 | } 279 | case _ => Seq.empty 280 | } 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/main/scala-3/com/typesafe/scalalogging/LoggerTakingImplicitImpl.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.{Marker, Logger as Underlying } 4 | 5 | trait LoggerTakingImplicitImpl[A] { 6 | def underlying: Underlying 7 | implicit val canLogEv: CanLog[A] 8 | 9 | 10 | // Error 11 | 12 | inline def error(inline message: String)(implicit inline a: A): Unit = 13 | ${LoggerTakingImplicitMacro.errorMessage('underlying, 'canLogEv, 'message)('a)} 14 | 15 | inline def error(inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 16 | ${LoggerTakingImplicitMacro.errorMessageCause('underlying, 'canLogEv, 'message, 'cause)('a)} 17 | 18 | inline def error(inline message: String, inline args: Any*)(implicit inline a: A): Unit = 19 | ${LoggerTakingImplicitMacro.errorMessageArgs('underlying, 'canLogEv, 'message, 'args)('a)} 20 | 21 | inline def error(inline marker: Marker, inline message: String)(implicit inline a: A): Unit = 22 | ${LoggerTakingImplicitMacro.errorMessageMarker('underlying, 'canLogEv, 'marker, 'message)('a)} 23 | 24 | inline def error(inline marker: Marker, inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 25 | ${LoggerTakingImplicitMacro.errorMessageCauseMarker('underlying, 'canLogEv, 'marker, 'message, 'cause)('a)} 26 | 27 | inline def error(inline marker: Marker, inline message: String, inline args: Any*)(implicit inline a: A): Unit = 28 | ${LoggerTakingImplicitMacro.errorMessageArgsMarker('underlying, 'canLogEv, 'marker,'message, 'args)('a)} 29 | 30 | inline def whenErrorEnabled(inline body: Unit)(implicit inline a: A): Unit = ${LoggerTakingImplicitMacro.errorCode('underlying, 'canLogEv, 'body)('a)} 31 | 32 | // Warn 33 | 34 | inline def warn(inline message: String)(implicit inline a: A): Unit = 35 | ${LoggerTakingImplicitMacro.warnMessage('underlying, 'canLogEv, 'message)('a)} 36 | 37 | inline def warn(inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 38 | ${LoggerTakingImplicitMacro.warnMessageCause('underlying, 'canLogEv, 'message, 'cause)('a)} 39 | 40 | inline def warn(inline message: String, inline args: Any*)(implicit inline a: A): Unit = 41 | ${LoggerTakingImplicitMacro.warnMessageArgs('underlying, 'canLogEv, 'message, 'args)('a)} 42 | 43 | inline def warn(inline marker: Marker, inline message: String)(implicit inline a: A): Unit = 44 | ${LoggerTakingImplicitMacro.warnMessageMarker('underlying, 'canLogEv, 'marker, 'message)('a)} 45 | 46 | inline def warn(inline marker: Marker, inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 47 | ${LoggerTakingImplicitMacro.warnMessageCauseMarker('underlying, 'canLogEv, 'marker, 'message, 'cause)('a)} 48 | 49 | inline def warn(inline marker: Marker, inline message: String, inline args: Any*)(implicit inline a: A): Unit = 50 | ${LoggerTakingImplicitMacro.warnMessageArgsMarker('underlying, 'canLogEv, 'marker,'message, 'args)('a)} 51 | 52 | inline def whenWarnEnabled(inline body: Unit)(implicit inline a: A): Unit = ${LoggerTakingImplicitMacro.warnCode('underlying, 'canLogEv, 'body)('a)} 53 | 54 | 55 | 56 | // Info 57 | 58 | inline def info(inline message: String)(implicit inline a: A): Unit = 59 | ${LoggerTakingImplicitMacro.infoMessage('underlying, 'canLogEv, 'message)('a)} 60 | 61 | inline def info(inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 62 | ${LoggerTakingImplicitMacro.infoMessageCause('underlying, 'canLogEv, 'message, 'cause)('a)} 63 | 64 | inline def info(inline message: String, inline args: Any*)(implicit inline a: A): Unit = 65 | ${LoggerTakingImplicitMacro.infoMessageArgs('underlying, 'canLogEv, 'message, 'args)('a)} 66 | 67 | inline def info(inline marker: Marker, inline message: String)(implicit inline a: A): Unit = 68 | ${LoggerTakingImplicitMacro.infoMessageMarker('underlying, 'canLogEv, 'marker, 'message)('a)} 69 | 70 | inline def info(inline marker: Marker, inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 71 | ${LoggerTakingImplicitMacro.infoMessageCauseMarker('underlying, 'canLogEv, 'marker, 'message, 'cause)('a)} 72 | 73 | inline def info(inline marker: Marker, inline message: String, inline args: Any*)(implicit inline a: A): Unit = 74 | ${LoggerTakingImplicitMacro.infoMessageArgsMarker('underlying, 'canLogEv, 'marker,'message, 'args)('a)} 75 | 76 | inline def whenInfoEnabled(inline body: Unit)(implicit inline a: A): Unit = ${LoggerTakingImplicitMacro.infoCode('underlying, 'canLogEv, 'body)('a)} 77 | 78 | 79 | // Debug 80 | 81 | inline def debug(inline message: String)(implicit inline a: A): Unit = 82 | ${LoggerTakingImplicitMacro.debugMessage('underlying, 'canLogEv, 'message)('a)} 83 | 84 | inline def debug(inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 85 | ${LoggerTakingImplicitMacro.debugMessageCause('underlying, 'canLogEv, 'message, 'cause)('a)} 86 | 87 | inline def debug(inline message: String, inline args: Any*)(implicit inline a: A): Unit = 88 | ${LoggerTakingImplicitMacro.debugMessageArgs('underlying, 'canLogEv, 'message, 'args)('a)} 89 | 90 | inline def debug(inline marker: Marker, inline message: String)(implicit inline a: A): Unit = 91 | ${LoggerTakingImplicitMacro.debugMessageMarker('underlying, 'canLogEv, 'marker, 'message)('a)} 92 | 93 | inline def debug(inline marker: Marker, inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 94 | ${LoggerTakingImplicitMacro.debugMessageCauseMarker('underlying, 'canLogEv, 'marker, 'message, 'cause)('a)} 95 | 96 | inline def debug(inline marker: Marker, inline message: String, inline args: Any*)(implicit inline a: A): Unit = 97 | ${LoggerTakingImplicitMacro.debugMessageArgsMarker('underlying, 'canLogEv, 'marker,'message, 'args)('a)} 98 | 99 | inline def whenDebugEnabled(inline body: Unit)(implicit inline a: A): Unit = ${LoggerTakingImplicitMacro.debugCode('underlying, 'canLogEv, 'body)('a)} 100 | 101 | // Trace 102 | 103 | inline def trace(inline message: String)(implicit inline a: A): Unit = 104 | ${LoggerTakingImplicitMacro.traceMessage('underlying, 'canLogEv, 'message)('a)} 105 | 106 | inline def trace(inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 107 | ${LoggerTakingImplicitMacro.traceMessageCause('underlying, 'canLogEv, 'message, 'cause)('a)} 108 | 109 | inline def trace(inline message: String, inline args: Any*)(implicit inline a: A): Unit = 110 | ${LoggerTakingImplicitMacro.traceMessageArgs('underlying, 'canLogEv, 'message, 'args)('a)} 111 | 112 | inline def trace(inline marker: Marker, inline message: String)(implicit inline a: A): Unit = 113 | ${LoggerTakingImplicitMacro.traceMessageMarker('underlying, 'canLogEv, 'marker, 'message)('a)} 114 | 115 | inline def trace(inline marker: Marker, inline message: String, inline cause: Throwable)(implicit inline a: A): Unit = 116 | ${LoggerTakingImplicitMacro.traceMessageCauseMarker('underlying, 'canLogEv, 'marker, 'message, 'cause)('a)} 117 | 118 | inline def trace(inline marker: Marker, inline message: String, inline args: Any*)(implicit inline a: A): Unit = 119 | ${LoggerTakingImplicitMacro.traceMessageArgsMarker('underlying, 'canLogEv, 'marker,'message, 'args)('a)} 120 | 121 | inline def whenTraceEnabled(inline body: Unit)(implicit inline a: A): Unit = ${LoggerTakingImplicitMacro.traceCode('underlying, 'canLogEv, 'body)('a)} 122 | } 123 | -------------------------------------------------------------------------------- /src/main/scala-3/com/typesafe/scalalogging/LoggerTakingImplicitMacro.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.{ Marker, Logger => Underlying } 4 | 5 | import scala.quoted.* 6 | 7 | private[scalalogging] object LoggerTakingImplicitMacro { 8 | 9 | // Error 10 | 11 | def errorMessage[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 12 | '{if($underlying.isErrorEnabled){ 13 | $underlying.error($canLogEv.logMessage($message, $a)) 14 | $canLogEv.afterLog($a) 15 | }} 16 | 17 | def errorMessageCause[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 18 | '{if ($underlying.isErrorEnabled) { 19 | $underlying.error($canLogEv.logMessage($message, $a), $cause) 20 | $canLogEv.afterLog($a) 21 | }} 22 | 23 | def errorMessageArgs[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 24 | val formatedArgs = LoggerMacro.formatArgs(args) 25 | if (formatedArgs.lengthIs == 1) 26 | '{if ($underlying.isErrorEnabled) { 27 | $underlying.error($canLogEv.logMessage($message, $a), ${formatedArgs.head}) 28 | $canLogEv.afterLog($a) 29 | }} 30 | else 31 | '{if ($underlying.isErrorEnabled) { 32 | $underlying.error($canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 33 | $canLogEv.afterLog($a) 34 | }} 35 | } 36 | 37 | def errorMessageMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 38 | '{if ($underlying.isErrorEnabled($marker)) { 39 | $underlying.error($marker, $canLogEv.logMessage($message, $a)) 40 | $canLogEv.afterLog($a) 41 | }} 42 | 43 | def errorMessageCauseMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 44 | '{if ($underlying.isErrorEnabled($marker)) { 45 | $underlying.error($marker, $canLogEv.logMessage($message, $a), $cause) 46 | $canLogEv.afterLog($a) 47 | }} 48 | 49 | def errorMessageArgsMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 50 | val formatedArgs = LoggerMacro.formatArgs(args) 51 | if (formatedArgs.lengthIs == 1) 52 | '{if ($underlying.isErrorEnabled($marker)) { 53 | $underlying.error($marker, $canLogEv.logMessage($message, $a), ${formatedArgs.head}) 54 | $canLogEv.afterLog($a) 55 | }} 56 | else 57 | '{if ($underlying.isErrorEnabled($marker)) { 58 | $underlying.error($marker, $canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 59 | $canLogEv.afterLog($a) 60 | }} 61 | } 62 | 63 | def errorCode[A:Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], body: Expr[Unit])(a: Expr[A])(using Quotes) = 64 | '{if ($underlying.isErrorEnabled) { 65 | $body 66 | $canLogEv.afterLog($a) 67 | }} 68 | 69 | // Warn 70 | 71 | def warnMessage[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 72 | '{if($underlying.isWarnEnabled){ 73 | $underlying.warn($canLogEv.logMessage($message, $a)) 74 | $canLogEv.afterLog($a) 75 | }} 76 | 77 | def warnMessageCause[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 78 | '{if ($underlying.isWarnEnabled) { 79 | $underlying.warn($canLogEv.logMessage($message, $a), $cause) 80 | $canLogEv.afterLog($a) 81 | }} 82 | 83 | def warnMessageArgs[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 84 | val formatedArgs = LoggerMacro.formatArgs(args) 85 | if (formatedArgs.lengthIs == 1) 86 | '{if ($underlying.isWarnEnabled) { 87 | $underlying.warn($canLogEv.logMessage($message, $a), ${formatedArgs.head}) 88 | $canLogEv.afterLog($a) 89 | }} 90 | else 91 | '{if ($underlying.isWarnEnabled) { 92 | $underlying.warn($canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 93 | $canLogEv.afterLog($a) 94 | }} 95 | } 96 | 97 | def warnMessageMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 98 | '{if ($underlying.isWarnEnabled($marker)) { 99 | $underlying.warn($marker, $canLogEv.logMessage($message, $a)) 100 | $canLogEv.afterLog($a) 101 | }} 102 | 103 | def warnMessageCauseMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 104 | '{if ($underlying.isWarnEnabled($marker)) { 105 | $underlying.warn($marker, $canLogEv.logMessage($message, $a), $cause) 106 | $canLogEv.afterLog($a) 107 | }} 108 | 109 | def warnMessageArgsMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 110 | val formatedArgs = LoggerMacro.formatArgs(args) 111 | if (formatedArgs.lengthIs == 1) 112 | '{if ($underlying.isWarnEnabled($marker)) { 113 | $underlying.warn($marker, $canLogEv.logMessage($message, $a), ${formatedArgs.head}) 114 | $canLogEv.afterLog($a) 115 | }} 116 | else 117 | '{if ($underlying.isWarnEnabled($marker)) { 118 | $underlying.warn($marker, $canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 119 | $canLogEv.afterLog($a) 120 | }} 121 | } 122 | 123 | def warnCode[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], body: Expr[Unit])(a: Expr[A])(using Quotes) = 124 | '{ if ($underlying.isWarnEnabled) { 125 | $body 126 | $canLogEv.afterLog($a) 127 | }} 128 | 129 | 130 | // Info 131 | 132 | def infoMessage[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 133 | '{if($underlying.isInfoEnabled){ 134 | $underlying.info($canLogEv.logMessage($message, $a)) 135 | $canLogEv.afterLog($a) 136 | }} 137 | 138 | def infoMessageCause[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 139 | '{if ($underlying.isInfoEnabled) { 140 | $underlying.info($canLogEv.logMessage($message, $a), $cause) 141 | $canLogEv.afterLog($a) 142 | }} 143 | 144 | def infoMessageArgs[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 145 | val formatedArgs = LoggerMacro.formatArgs(args) 146 | if (formatedArgs.lengthIs == 1) 147 | '{if ($underlying.isInfoEnabled) { 148 | $underlying.info($canLogEv.logMessage($message, $a), ${formatedArgs.head}) 149 | $canLogEv.afterLog($a) 150 | }} 151 | else 152 | '{if ($underlying.isInfoEnabled) { 153 | $underlying.info($canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 154 | $canLogEv.afterLog($a) 155 | }} 156 | } 157 | 158 | def infoMessageMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 159 | '{if ($underlying.isInfoEnabled($marker)) { 160 | $underlying.info($marker, $canLogEv.logMessage($message, $a)) 161 | $canLogEv.afterLog($a) 162 | }} 163 | 164 | def infoMessageCauseMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 165 | '{if ($underlying.isInfoEnabled($marker)) { 166 | $underlying.info($marker, $canLogEv.logMessage($message, $a), $cause) 167 | $canLogEv.afterLog($a) 168 | }} 169 | 170 | def infoMessageArgsMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 171 | val formatedArgs = LoggerMacro.formatArgs(args) 172 | if (formatedArgs.lengthIs == 1) 173 | '{if ($underlying.isInfoEnabled($marker)) { 174 | $underlying.info($marker, $canLogEv.logMessage($message, $a), ${formatedArgs.head}) 175 | $canLogEv.afterLog($a) 176 | }} 177 | else 178 | '{if ($underlying.isInfoEnabled($marker)) { 179 | $underlying.info($marker, $canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 180 | $canLogEv.afterLog($a) 181 | }} 182 | } 183 | 184 | def infoCode[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], body: Expr[Unit])(a: Expr[A])(using Quotes) = 185 | '{ if ($underlying.isInfoEnabled) { 186 | $body 187 | $canLogEv.afterLog($a) 188 | }} 189 | 190 | 191 | // Debug 192 | 193 | def debugMessage[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 194 | '{if($underlying.isDebugEnabled){ 195 | $underlying.debug($canLogEv.logMessage($message, $a)) 196 | $canLogEv.afterLog($a) 197 | }} 198 | 199 | def debugMessageCause[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 200 | '{if ($underlying.isDebugEnabled) { 201 | $underlying.debug($canLogEv.logMessage($message, $a), $cause) 202 | $canLogEv.afterLog($a) 203 | }} 204 | 205 | def debugMessageArgs[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 206 | val formatedArgs = LoggerMacro.formatArgs(args) 207 | if (formatedArgs.lengthIs == 1) 208 | '{if ($underlying.isDebugEnabled) { 209 | $underlying.debug($canLogEv.logMessage($message, $a), ${formatedArgs.head}) 210 | $canLogEv.afterLog($a) 211 | }} 212 | else 213 | '{if ($underlying.isDebugEnabled) { 214 | $underlying.debug($canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 215 | $canLogEv.afterLog($a) 216 | }} 217 | } 218 | 219 | def debugMessageMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 220 | '{if ($underlying.isDebugEnabled($marker)) { 221 | $underlying.debug($marker, $canLogEv.logMessage($message, $a)) 222 | $canLogEv.afterLog($a) 223 | }} 224 | 225 | def debugMessageCauseMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 226 | '{if ($underlying.isDebugEnabled($marker)) { 227 | $underlying.debug($marker, $canLogEv.logMessage($message, $a), $cause) 228 | $canLogEv.afterLog($a) 229 | }} 230 | 231 | def debugMessageArgsMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 232 | val formatedArgs = LoggerMacro.formatArgs(args) 233 | if (formatedArgs.lengthIs == 1) 234 | '{if ($underlying.isDebugEnabled($marker)) { 235 | $underlying.debug($marker, $canLogEv.logMessage($message, $a), ${formatedArgs.head}) 236 | $canLogEv.afterLog($a) 237 | }} 238 | else 239 | '{if ($underlying.isDebugEnabled($marker)) { 240 | $underlying.debug($marker, $canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 241 | $canLogEv.afterLog($a) 242 | }} 243 | } 244 | 245 | def debugCode[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], body: Expr[Unit])(a: Expr[A])(using Quotes) = 246 | '{ if ($underlying.isDebugEnabled) { 247 | $body 248 | $canLogEv.afterLog($a) 249 | }} 250 | 251 | 252 | // Trace 253 | 254 | def traceMessage[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 255 | '{if($underlying.isTraceEnabled){ 256 | $underlying.trace($canLogEv.logMessage($message, $a)) 257 | $canLogEv.afterLog($a) 258 | }} 259 | 260 | def traceMessageCause[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 261 | '{if ($underlying.isTraceEnabled) { 262 | $underlying.trace($canLogEv.logMessage($message, $a), $cause) 263 | $canLogEv.afterLog($a) 264 | }} 265 | 266 | def traceMessageArgs[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 267 | val formatedArgs = LoggerMacro.formatArgs(args) 268 | if (formatedArgs.lengthIs == 1) 269 | '{if ($underlying.isTraceEnabled) { 270 | $underlying.trace($canLogEv.logMessage($message, $a), ${formatedArgs.head}) 271 | $canLogEv.afterLog($a) 272 | }} 273 | else 274 | '{if ($underlying.isTraceEnabled) { 275 | $underlying.trace($canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 276 | $canLogEv.afterLog($a) 277 | }} 278 | } 279 | 280 | def traceMessageMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String])(a: Expr[A])(using Quotes): Expr[Unit] = 281 | '{if ($underlying.isTraceEnabled($marker)) { 282 | $underlying.trace($marker, $canLogEv.logMessage($message, $a)) 283 | $canLogEv.afterLog($a) 284 | }} 285 | 286 | def traceMessageCauseMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], cause: Expr[Throwable])(a: Expr[A])(using Quotes): Expr[Unit] = 287 | '{if ($underlying.isTraceEnabled($marker)) { 288 | $underlying.trace($marker, $canLogEv.logMessage($message, $a), $cause) 289 | $canLogEv.afterLog($a) 290 | }} 291 | 292 | def traceMessageArgsMarker[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], marker: Expr[Marker], message: Expr[String], args: Expr[Seq[Any]])(a: Expr[A])(using Quotes): Expr[Unit] = { 293 | val formatedArgs = LoggerMacro.formatArgs(args) 294 | if (formatedArgs.lengthIs == 1) 295 | '{if ($underlying.isTraceEnabled($marker)) { 296 | $underlying.trace($marker, $canLogEv.logMessage($message, $a), ${formatedArgs.head}) 297 | $canLogEv.afterLog($a) 298 | }} 299 | else 300 | '{if ($underlying.isTraceEnabled($marker)) { 301 | $underlying.trace($marker, $canLogEv.logMessage($message, $a), ${Expr.ofSeq(formatedArgs)}*) 302 | $canLogEv.afterLog($a) 303 | }} 304 | } 305 | 306 | def traceCode[A: Type](underlying: Expr[Underlying], canLogEv: Expr[CanLog[A]], body: Expr[Unit])(a: Expr[A])(using Quotes) = 307 | '{ if ($underlying.isTraceEnabled) { 308 | $body 309 | $canLogEv.afterLog($a) 310 | }} 311 | } -------------------------------------------------------------------------------- /src/main/scala/com/typesafe/scalalogging/Logger.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.{ LoggerFactory, Logger => Underlying } 4 | import scala.reflect.ClassTag 5 | 6 | /** 7 | * Companion for [[Logger]], providing a factory for [[Logger]]s. 8 | */ 9 | object Logger { 10 | 11 | /** 12 | * Create a [[Logger]] wrapping the given underlying `org.slf4j.Logger`. 13 | */ 14 | def apply(underlying: Underlying): Logger = 15 | new Logger(underlying) 16 | 17 | /** 18 | * Create a [[LoggerTakingImplicit]] wrapping the given underlying `org.slf4j.Logger`. 19 | */ 20 | def takingImplicit[A](underlying: Underlying)(implicit ev: CanLog[A]): LoggerTakingImplicit[A] = 21 | new LoggerTakingImplicit[A](underlying) 22 | 23 | /** 24 | * Create a [[Logger]] for the given name. 25 | * Example: 26 | * {{{ 27 | * val logger = Logger("application") 28 | * }}} 29 | */ 30 | def apply(name: String): Logger = 31 | new Logger(LoggerFactory.getLogger(name)) 32 | 33 | /** 34 | * Create a [[LoggerTakingImplicit]] for the given name. 35 | * Example: 36 | * {{{ 37 | * val logger = Logger.takingImplicit[CorrelationId]("application") 38 | * }}} 39 | */ 40 | def takingImplicit[A](name: String)(implicit ev: CanLog[A]): LoggerTakingImplicit[A] = 41 | new LoggerTakingImplicit[A](LoggerFactory.getLogger(name)) 42 | 43 | /** 44 | * Create a [[Logger]] wrapping the created underlying `org.slf4j.Logger`. 45 | */ 46 | def apply(clazz: Class[_]): Logger = 47 | new Logger(LoggerFactory.getLogger(clazz.getName)) 48 | 49 | /** 50 | * Create a [[LoggerTakingImplicit]] wrapping the created underlying `org.slf4j.Logger`. 51 | */ 52 | def takingImplicit[A](clazz: Class[_])(implicit ev: CanLog[A]): LoggerTakingImplicit[A] = 53 | new LoggerTakingImplicit[A](LoggerFactory.getLogger(clazz.getName)) 54 | 55 | /** 56 | * Create a [[Logger]] for the runtime class wrapped by the implicit class 57 | * tag parameter. 58 | * Example: 59 | * {{{ 60 | * val logger = Logger[MyClass] 61 | * }}} 62 | */ 63 | def apply[T](implicit ct: ClassTag[T]): Logger = 64 | new Logger(LoggerFactory.getLogger(ct.runtimeClass.getName.stripSuffix("$"))) 65 | 66 | /** 67 | * Create a [[LoggerTakingImplicit]] for the runtime class wrapped by the implicit class 68 | * tag parameter. 69 | * Example: 70 | * {{{ 71 | * val logger = Logger.takingImplicit[MyClass, CorrelationId] 72 | * }}} 73 | */ 74 | def takingImplicit[T, A](implicit ct: ClassTag[T], ev: CanLog[A]): LoggerTakingImplicit[A] = 75 | new LoggerTakingImplicit[A](LoggerFactory.getLogger(ct.runtimeClass.getName.stripSuffix("$"))) 76 | } 77 | 78 | /** 79 | * Implementation of a fast logger based on macros and an underlying `org.slf4j.Logger`. 80 | */ 81 | @SerialVersionUID(538248225L) 82 | final class Logger private[scalalogging] (val underlying: Underlying) extends LoggerImpl with Serializable -------------------------------------------------------------------------------- /src/main/scala/com/typesafe/scalalogging/LoggerTakingImplicit.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.{ Logger => Underlying } 4 | 5 | trait CanLog[A] { 6 | def logMessage(originalMsg: String, context: A): String 7 | def afterLog(context: A): Unit = { 8 | val _ = context 9 | } 10 | def getContext()(implicit context: A): A = context 11 | } 12 | 13 | @SerialVersionUID(957385465L) 14 | class LoggerTakingImplicit[A] private[scalalogging] (val underlying: Underlying)(implicit val canLogEv: CanLog[A]) extends LoggerTakingImplicitImpl[A] with Serializable -------------------------------------------------------------------------------- /src/main/scala/com/typesafe/scalalogging/Logging.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.slf4j.LoggerFactory 4 | 5 | /** 6 | * Defines `logger` as a lazy value initialized with an underlying `org.slf4j.Logger` 7 | * named according to the class into which this trait is mixed. 8 | */ 9 | trait LazyLogging { 10 | 11 | @transient 12 | protected lazy val logger: Logger = 13 | Logger(LoggerFactory.getLogger(getClass.getName)) 14 | } 15 | 16 | /** 17 | * Defines `logger` as a value initialized with an underlying `org.slf4j.Logger` 18 | * named according to the class into which this trait is mixed. 19 | */ 20 | trait StrictLogging { 21 | 22 | protected val logger: Logger = 23 | Logger(LoggerFactory.getLogger(getClass.getName)) 24 | } 25 | 26 | /** 27 | * Defines an abstract `logger` that will be useful while writing some trait which needs access 28 | * to any logger without deciding on an specific implementation. 29 | */ 30 | trait AnyLogging { 31 | 32 | protected val logger: Logger 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/com/typesafe/scalalogging/package.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe 2 | 3 | package object scalalogging { 4 | 5 | type Iterable[+A] = scala.collection.immutable.Iterable[A] 6 | 7 | type Seq[+A] = scala.collection.immutable.Seq[A] 8 | 9 | type IndexedSeq[+A] = scala.collection.immutable.IndexedSeq[A] 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %date{HH:mm:ss} %-5level %logger{0} {%class %method} - %msg%n 6 | 7 | 8 | 9 | 10 | ${log-file:-scala-logging.log} 11 | 12 | %date{HH:mm:ss} %-5level %logger{0} {%class %method} - %msg%n 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/scala/com/typesafe/scalalogging/LoggerSpec.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import java.io._ 4 | import org.slf4j.{ Logger => Underlying } 5 | import org.scalatest.matchers.should.Matchers 6 | import org.scalatest.wordspec.AnyWordSpec 7 | import org.scalatestplus.mockito.MockitoSugar 8 | import org.mockito.ArgumentMatchers._ 9 | import org.mockito.Mockito._ 10 | 11 | trait Varargs { 12 | // TODO: we used to wrap in List(...): _*, which I assume was to force the varags method to be chosen. 13 | // I encapsulated that here in something that works across 2.12/2.13. 14 | def forceVarargs[T](xs: T*): scala.Seq[T] = scala.Seq(xs: _*) 15 | } 16 | 17 | class LoggerSpec extends AnyWordSpec with Matchers with Varargs with MockitoSugar { 18 | 19 | // Error 20 | 21 | "Calling error with a message" should { 22 | 23 | "call the underlying logger's error method if the error level is enabled" in { 24 | val f = fixture(_.isErrorEnabled, isEnabled = true) 25 | import f._ 26 | logger.error(msg) 27 | verify(underlying).error(msg) 28 | } 29 | 30 | "not call the underlying logger's error method if the error level is not enabled" in { 31 | val f = fixture(_.isErrorEnabled, isEnabled = false) 32 | import f._ 33 | logger.error(msg) 34 | verify(underlying, never).error(any[String]) 35 | } 36 | } 37 | 38 | "Calling error with an interpolated message" should { 39 | 40 | "call the underlying logger's error method with arguments if the error level is enabled" in { 41 | val f = fixture(_.isErrorEnabled, isEnabled = true) 42 | import f._ 43 | logger.error(s"msg $arg1 $arg2 $arg3") 44 | verify(underlying).error("msg {} {} {}", arg1, arg2, arg3) 45 | } 46 | 47 | "call the underlying logger's error method with two arguments if the error level is enabled" in { 48 | val f = fixture(_.isErrorEnabled, isEnabled = true) 49 | import f._ 50 | logger.error(s"msg $arg1 $arg2") 51 | verify(underlying).error("msg {} {}", forceVarargs(arg1, arg2): _*) 52 | } 53 | 54 | } 55 | 56 | "Calling error with a message and cause" should { 57 | 58 | "call the underlying logger's error method if the error level is enabled" in { 59 | val f = fixture(_.isErrorEnabled, isEnabled = true) 60 | import f._ 61 | logger.error(msg, cause) 62 | verify(underlying).error(msg, cause) 63 | } 64 | 65 | "not call the underlying logger's error method if the error level is not enabled" in { 66 | val f = fixture(_.isErrorEnabled, isEnabled = false) 67 | import f._ 68 | logger.error(msg, cause) 69 | verify(underlying, never).error(any[String], any[Object]) 70 | } 71 | } 72 | 73 | "Calling error with a message and parameters" should { 74 | 75 | "call the underlying logger's error method if the error level is enabled" in { 76 | val f = fixture(_.isErrorEnabled, isEnabled = true) 77 | import f._ 78 | logger.error(msg, arg1) 79 | verify(underlying).error(msg, arg1) 80 | logger.error(msg, arg1, arg2) 81 | verify(underlying).error(msg, forceVarargs(arg1, arg2): _*) 82 | logger.error(msg, arg1, arg2, arg3) 83 | verify(underlying).error(msg, arg1, arg2, arg3) 84 | } 85 | 86 | "not call the underlying logger's error method if the error level is not enabled" in { 87 | val f = fixture(_.isErrorEnabled, isEnabled = false) 88 | import f._ 89 | logger.error(msg, arg1) 90 | verify(underlying, never).error(msg, arg1) 91 | logger.error(msg, arg1, arg2) 92 | verify(underlying, never).error(msg, forceVarargs(arg1, arg2): _*) 93 | logger.error(msg, arg1, arg2, arg3) 94 | verify(underlying, never).error(msg, arg1, arg2, arg3) 95 | } 96 | } 97 | 98 | // Warn 99 | 100 | "Calling warn with a message" should { 101 | 102 | "call the underlying logger's warn method if the warn level is enabled" in { 103 | val f = fixture(_.isWarnEnabled, isEnabled = true) 104 | import f._ 105 | logger.warn(msg) 106 | verify(underlying).warn(msg) 107 | } 108 | 109 | "not call the underlying logger's warn method if the warn level is not enabled" in { 110 | val f = fixture(_.isWarnEnabled, isEnabled = false) 111 | import f._ 112 | logger.warn(msg) 113 | verify(underlying, never).warn(any[String]) 114 | } 115 | } 116 | 117 | "Calling warn with an interpolated message" should { 118 | 119 | "call the underlying logger's warn method if the warn level is enabled" in { 120 | val f = fixture(_.isWarnEnabled, isEnabled = true) 121 | import f._ 122 | logger.warn(s"msg $arg1 $arg2 $arg3") 123 | verify(underlying).warn("msg {} {} {}", arg1, arg2, arg3) 124 | } 125 | 126 | "call the underlying logger's warn method with two arguments if the warn level is enabled" in { 127 | val f = fixture(_.isWarnEnabled, isEnabled = true) 128 | import f._ 129 | logger.warn(s"msg $arg1 $arg2") 130 | verify(underlying).warn("msg {} {}", forceVarargs(arg1, arg2): _*) 131 | } 132 | } 133 | 134 | "Calling warn with a message and cause" should { 135 | 136 | "call the underlying logger's warn method if the warn level is enabled" in { 137 | val f = fixture(_.isWarnEnabled, isEnabled = true) 138 | import f._ 139 | logger.warn(msg, cause) 140 | verify(underlying).warn(msg, cause) 141 | } 142 | 143 | "not call the underlying logger's warn method if the warn level is not enabled" in { 144 | val f = fixture(_.isWarnEnabled, isEnabled = false) 145 | import f._ 146 | logger.warn(msg, cause) 147 | verify(underlying, never).warn(any[String], any[Object]) 148 | } 149 | } 150 | 151 | "Calling warn with a message and parameters" should { 152 | 153 | "call the underlying logger's warn method if the warn level is enabled" in { 154 | val f = fixture(_.isWarnEnabled, isEnabled = true) 155 | import f._ 156 | logger.warn(msg, arg1) 157 | verify(underlying).warn(msg, arg1) 158 | logger.warn(msg, arg1, arg2) 159 | verify(underlying).warn(msg, forceVarargs(arg1, arg2): _*) 160 | logger.warn(msg, arg1, arg2, arg3) 161 | verify(underlying).warn(msg, arg1, arg2, arg3) 162 | } 163 | 164 | "not call the underlying logger's warn method if the warn level is not enabled" in { 165 | val f = fixture(_.isWarnEnabled, isEnabled = false) 166 | import f._ 167 | logger.warn(msg, arg1) 168 | verify(underlying, never).warn(msg, arg1) 169 | logger.warn(msg, arg1, arg2) 170 | verify(underlying, never).warn(msg, forceVarargs(arg1, arg2): _*) 171 | logger.warn(msg, arg1, arg2, arg3) 172 | verify(underlying, never).warn(msg, arg1, arg2, arg3) 173 | } 174 | } 175 | 176 | // Info 177 | 178 | "Calling info with a message" should { 179 | 180 | "call the underlying logger's info method if the info level is enabled" in { 181 | val f = fixture(_.isInfoEnabled, isEnabled = true) 182 | import f._ 183 | logger.info(msg) 184 | verify(underlying).info(msg) 185 | } 186 | 187 | "not call the underlying logger's info method if the info level is not enabled" in { 188 | val f = fixture(_.isInfoEnabled, isEnabled = false) 189 | import f._ 190 | logger.info(msg) 191 | verify(underlying, never).info(any[String]) 192 | } 193 | } 194 | 195 | "Calling info with an interpolated message" should { 196 | 197 | "call the underlying logger's info method if the info level is enabled" in { 198 | val f = fixture(_.isInfoEnabled, isEnabled = true) 199 | import f._ 200 | logger.info(s"msg $arg1 $arg2 $arg3") 201 | verify(underlying).info("msg {} {} {}", arg1, arg2, arg3) 202 | } 203 | 204 | "call the underlying logger's info method with two arguments if the info level is enabled" in { 205 | val f = fixture(_.isInfoEnabled, isEnabled = true) 206 | import f._ 207 | logger.info(s"msg $arg1 $arg2") 208 | verify(underlying).info("msg {} {}", forceVarargs(arg1, arg2): _*) 209 | } 210 | } 211 | 212 | "Calling info with a message and cause" should { 213 | 214 | "call the underlying logger's info method if the info level is enabled" in { 215 | val f = fixture(_.isInfoEnabled, isEnabled = true) 216 | import f._ 217 | logger.info(msg, cause) 218 | verify(underlying).info(msg, cause) 219 | } 220 | 221 | "not call the underlying logger's info method if the info level is not enabled" in { 222 | val f = fixture(_.isInfoEnabled, isEnabled = false) 223 | import f._ 224 | logger.info(msg, cause) 225 | verify(underlying, never).info(any[String], any[Object]) 226 | } 227 | } 228 | 229 | "Calling info with a message and parameters" should { 230 | 231 | "call the underlying logger's info method if the info level is enabled" in { 232 | val f = fixture(_.isInfoEnabled, isEnabled = true) 233 | import f._ 234 | logger.info(msg, arg1) 235 | verify(underlying).info(msg, arg1) 236 | logger.info(msg, arg1, arg2) 237 | verify(underlying).info(msg, forceVarargs(arg1, arg2): _*) 238 | logger.info(msg, arg1, arg2, arg3) 239 | verify(underlying).info(msg, arg1, arg2, arg3) 240 | } 241 | 242 | "not call the underlying logger's info method if the info level is not enabled" in { 243 | val f = fixture(_.isInfoEnabled, isEnabled = false) 244 | import f._ 245 | logger.info(msg, arg1) 246 | verify(underlying, never).info(msg, arg1) 247 | logger.info(msg, arg1, arg2) 248 | verify(underlying, never).info(msg, forceVarargs(arg1, arg2): _*) 249 | logger.info(msg, arg1, arg2, arg3) 250 | verify(underlying, never).info(msg, arg1, arg2, arg3) 251 | } 252 | } 253 | 254 | // Debug 255 | 256 | "Calling debug with a message" should { 257 | 258 | "call the underlying logger's debug method if the debug level is enabled" in { 259 | val f = fixture(_.isDebugEnabled, isEnabled = true) 260 | import f._ 261 | logger.debug(msg) 262 | verify(underlying).debug(msg) 263 | } 264 | 265 | "not call the underlying logger's debug method if the debug level is not enabled" in { 266 | val f = fixture(_.isDebugEnabled, isEnabled = false) 267 | import f._ 268 | logger.debug(msg) 269 | verify(underlying, never).debug(any[String]) 270 | } 271 | } 272 | "Calling debug with an interpolated message" should { 273 | 274 | "call the underlying logger's debug method if the debug level is enabled" in { 275 | val f = fixture(_.isDebugEnabled, isEnabled = true) 276 | import f._ 277 | logger.debug(s"msg $arg1 $arg2 $arg3") 278 | verify(underlying).debug("msg {} {} {}", arg1, arg2, arg3) 279 | } 280 | 281 | "call the underlying logger's debug method with two arguments if the debug level is enabled" in { 282 | val f = fixture(_.isDebugEnabled, isEnabled = true) 283 | import f._ 284 | logger.debug(s"msg $arg1 $arg2") 285 | verify(underlying).debug("msg {} {}", forceVarargs(arg1, arg2): _*) 286 | } 287 | } 288 | 289 | "Calling debug with a message and cause" should { 290 | 291 | "call the underlying logger's debug method if the debug level is enabled" in { 292 | val f = fixture(_.isDebugEnabled, isEnabled = true) 293 | import f._ 294 | logger.debug(msg, cause) 295 | verify(underlying).debug(msg, cause) 296 | } 297 | 298 | "not call the underlying logger's debug method if the debug level is not enabled" in { 299 | val f = fixture(_.isDebugEnabled, isEnabled = false) 300 | import f._ 301 | logger.debug(msg, cause) 302 | verify(underlying, never).debug(any[String], any[Object]) 303 | } 304 | } 305 | 306 | "Calling debug with a message and parameters" should { 307 | 308 | "call the underlying logger's debug method if the debug level is enabled" in { 309 | val f = fixture(_.isDebugEnabled, isEnabled = true) 310 | import f._ 311 | logger.debug(msg, arg1) 312 | verify(underlying).debug(msg, arg1) 313 | logger.debug(msg, arg1, arg2) 314 | verify(underlying).debug(msg, forceVarargs(arg1, arg2): _*) 315 | logger.debug(msg, arg1, arg2, arg3) 316 | verify(underlying).debug(msg, arg1, arg2, arg3) 317 | } 318 | 319 | "not call the underlying logger's debug method if the debug level is not enabled" in { 320 | val f = fixture(_.isDebugEnabled, isEnabled = false) 321 | import f._ 322 | logger.debug(msg, arg1) 323 | verify(underlying, never).debug(msg, arg1) 324 | logger.debug(msg, arg1, arg2) 325 | verify(underlying, never).debug(msg, forceVarargs(arg1, arg2): _*) 326 | logger.debug(msg, arg1, arg2, arg3) 327 | verify(underlying, never).debug(msg, arg1, arg2, arg3) 328 | } 329 | } 330 | 331 | // Trace 332 | 333 | "Calling trace with a message" should { 334 | 335 | "call the underlying logger's trace method if the trace level is enabled" in { 336 | val f = fixture(_.isTraceEnabled, isEnabled = true) 337 | import f._ 338 | logger.trace(msg) 339 | verify(underlying).trace(msg) 340 | } 341 | 342 | "not call the underlying logger's trace method if the trace level is not enabled" in { 343 | val f = fixture(_.isTraceEnabled, isEnabled = false) 344 | import f._ 345 | logger.trace(msg) 346 | verify(underlying, never).trace(any[String]) 347 | } 348 | } 349 | 350 | "Calling trace with an interpolated message" should { 351 | 352 | "call the underlying logger's trace method if the trace level is enabled" in { 353 | val f = fixture(_.isTraceEnabled, isEnabled = true) 354 | import f._ 355 | logger.trace(s"msg $arg1 $arg2 $arg3") 356 | verify(underlying).trace("msg {} {} {}", arg1, arg2, arg3) 357 | } 358 | 359 | "call the underlying logger's trace method with two arguments if the trace level is enabled" in { 360 | val f = fixture(_.isTraceEnabled, isEnabled = true) 361 | import f._ 362 | logger.trace(s"msg $arg1 $arg2") 363 | verify(underlying).trace("msg {} {}", forceVarargs(arg1, arg2): _*) 364 | } 365 | } 366 | 367 | "Calling trace with a message and cause" should { 368 | 369 | "call the underlying logger's trace method if the trace level is enabled" in { 370 | val f = fixture(_.isTraceEnabled, isEnabled = true) 371 | import f._ 372 | logger.trace(msg, cause) 373 | verify(underlying).trace(msg, cause) 374 | } 375 | 376 | "not call the underlying logger's trace method if the trace level is not enabled" in { 377 | val f = fixture(_.isTraceEnabled, isEnabled = false) 378 | import f._ 379 | logger.trace(msg, cause) 380 | verify(underlying, never).trace(any[String], any[Object]) 381 | } 382 | } 383 | 384 | "Calling trace with a message and parameters" should { 385 | 386 | "call the underlying logger's trace method if the trace level is enabled" in { 387 | val f = fixture(_.isTraceEnabled, isEnabled = true) 388 | import f._ 389 | logger.trace(msg, arg1) 390 | verify(underlying).trace(msg, arg1) 391 | logger.trace(msg, arg1, arg2) 392 | verify(underlying).trace(msg, forceVarargs(arg1, arg2): _*) 393 | logger.trace(msg, arg1, arg2, arg3) 394 | verify(underlying).trace(msg, arg1, arg2, arg3) 395 | } 396 | 397 | "not call the underlying logger's trace method if the trace level is not enabled" in { 398 | val f = fixture(_.isTraceEnabled, isEnabled = false) 399 | import f._ 400 | logger.trace(msg, arg1) 401 | verify(underlying, never).trace(msg, arg1) 402 | logger.trace(msg, arg1, arg2) 403 | verify(underlying, never).trace(msg, forceVarargs(arg1, arg2): _*) 404 | logger.trace(msg, arg1, arg2, arg3) 405 | verify(underlying, never).trace(msg, arg1, arg2, arg3) 406 | } 407 | } 408 | 409 | // Interpolator destructuring corner cases 410 | 411 | "Logging a message using the standard string interpolator" should { 412 | 413 | "call the underlying format method with boxed versions of value arguments" in { 414 | val f = fixture(_.isErrorEnabled, isEnabled = true) 415 | import f._ 416 | logger.error(s"msg ${1}") 417 | verify(underlying).error("msg {}", 1.asInstanceOf[AnyRef]) 418 | } 419 | 420 | "call the underlying format method with boxed versions of arguments of type Any" in { 421 | val f = fixture(_.isErrorEnabled, isEnabled = true) 422 | import f._ 423 | logger.error(s"msg ${1.asInstanceOf[Any]}") 424 | verify(underlying).error("msg {}", 1.asInstanceOf[AnyRef]) 425 | } 426 | 427 | "call the underlying format method escaping literal format anchors" in { 428 | val f = fixture(_.isErrorEnabled, isEnabled = true) 429 | import f._ 430 | logger.error(s"foo {} bar $arg1") 431 | verify(underlying).error("foo \\{} bar {}", arg1) 432 | } 433 | 434 | "call the underlying method without escaping format anchors when the message has no interpolations" in { 435 | val f = fixture(_.isErrorEnabled, isEnabled = true) 436 | import f._ 437 | logger.error(s"foo {} bar") 438 | verify(underlying).error("foo {} bar") 439 | } 440 | 441 | "call the underlying format method when the interpolated string contains escape sequences" in { 442 | val f = fixture(_.isErrorEnabled, isEnabled = true) 443 | import f._ 444 | logger.error(s"foo\nbar $arg1") 445 | verify(underlying).error(s"foo\nbar {}", arg1) 446 | } 447 | 448 | "call the underlying format method when the interpolated string is triple quoted and contains escape sequences" in { 449 | val f = fixture(_.isErrorEnabled, isEnabled = true) 450 | import f._ 451 | logger.error(s"""foo\nbar $arg1""") 452 | verify(underlying).error(s"""foo\nbar {}""", arg1) 453 | } 454 | 455 | "call the underlying format method when interpolated argument is a Throwable" in { 456 | val f = fixture(_.isErrorEnabled, isEnabled = true) 457 | import f._ 458 | logger.error(s"""foo\nbar $arg7""") 459 | verify(underlying).error(s"""foo\nbar {}""", arg7ref) 460 | } 461 | } 462 | 463 | "Logging a message using slf4 interpolator and Any args" should { 464 | "map args to AnyRef for 2 args" in { 465 | val f = fixture(_.isErrorEnabled, isEnabled = true) 466 | import f._ 467 | logger.error("foo {}, bar {}", arg4, arg5) 468 | verify(underlying).error("foo {}, bar {}", forceVarargs(arg4ref, arg5ref): _*) 469 | } 470 | 471 | "map args to AnyRef for non 2 args" in { 472 | val f = fixture(_.isErrorEnabled, isEnabled = true) 473 | import f._ 474 | logger.error("foo {}", arg4) 475 | verify(underlying).error("foo {}", arg4ref) 476 | logger.error("foo {}, bar {}, {}", arg4, arg5, arg6) 477 | verify(underlying).error("foo {}, bar {}, {}", arg4ref, arg5ref, arg6ref) 478 | } 479 | } 480 | 481 | "Serializing Logger" should { 482 | 483 | def serialize(logger: Logger): Array[Byte] = { 484 | val byteArrayStream = new ByteArrayOutputStream 485 | val out = new ObjectOutputStream(byteArrayStream) 486 | 487 | out.writeObject(logger) 488 | out.close() 489 | byteArrayStream.close() 490 | 491 | byteArrayStream.toByteArray 492 | } 493 | 494 | def deserialize(array: Array[Byte]): Logger = { 495 | val byteArrayStream = new ByteArrayInputStream(array) 496 | val in = new ObjectInputStream(byteArrayStream) 497 | 498 | val logger = in.readObject.asInstanceOf[Logger] 499 | in.close() 500 | byteArrayStream.close() 501 | 502 | logger 503 | } 504 | 505 | "be usable after deserialization" in { 506 | val logger = deserialize(serialize(Logger(org.slf4j.LoggerFactory.getLogger("test")))) 507 | 508 | logger.trace("Back from deserialization") 509 | } 510 | 511 | "constructed by explicit class and be usable after deserialization" in { 512 | val logger = deserialize(serialize(Logger(classOf[LoggerSpec]))) 513 | 514 | logger.trace("Back from deserialization") 515 | } 516 | 517 | "constructed by implicit class tag and be usable after deserialization" in { 518 | val logger = deserialize(serialize(Logger[LoggerSpec])) 519 | 520 | logger.trace("Back from deserialization") 521 | } 522 | } 523 | 524 | "Serializing LoggerTakingImplicit" should { 525 | case class CorrelationId(value: String) 526 | implicit val correlationId: CorrelationId = CorrelationId(value = "correlationId") 527 | 528 | implicit case object canLogCorrelationId extends CanLog[CorrelationId] { 529 | def logMessage(originalMsg: String, a: CorrelationId): String = s"${a.value} $originalMsg" 530 | } 531 | 532 | def serialize[A](logger: LoggerTakingImplicit[A]): Array[Byte] = { 533 | val byteArrayStream = new ByteArrayOutputStream 534 | val out = new ObjectOutputStream(byteArrayStream) 535 | 536 | out.writeObject(logger) 537 | out.close() 538 | byteArrayStream.close() 539 | 540 | byteArrayStream.toByteArray 541 | } 542 | 543 | def deserialize[A](array: Array[Byte]): LoggerTakingImplicit[A] = { 544 | val byteArrayStream = new ByteArrayInputStream(array) 545 | val in = new ObjectInputStream(byteArrayStream) 546 | 547 | val logger = in.readObject.asInstanceOf[LoggerTakingImplicit[A]] 548 | in.close() 549 | byteArrayStream.close() 550 | 551 | logger 552 | } 553 | 554 | "be usable after deserialization" in { 555 | val logger = 556 | deserialize[CorrelationId]( 557 | serialize[CorrelationId]( 558 | Logger.takingImplicit[CorrelationId]( 559 | org.slf4j.LoggerFactory.getLogger("test")))) 560 | 561 | logger.trace("Back from deserialization") 562 | } 563 | 564 | "constructed by explicit class and be usable after deserialization" in { 565 | val logger = 566 | deserialize[CorrelationId]( 567 | serialize[CorrelationId]( 568 | Logger.takingImplicit[CorrelationId]( 569 | classOf[LoggerSpec]))) 570 | 571 | logger.trace("Back from deserialization") 572 | } 573 | 574 | "constructed by implicit class tag and be usable after deserialization" in { 575 | val logger = 576 | deserialize[CorrelationId]( 577 | serialize[CorrelationId]( 578 | Logger.takingImplicit[LoggerSpec, CorrelationId])) 579 | 580 | logger.trace("Back from deserialization") 581 | } 582 | } 583 | 584 | private def fixture(p: Underlying => Boolean, isEnabled: Boolean) = new LoggerF(p, isEnabled) 585 | private class LoggerF(p: Underlying => Boolean, isEnabled: Boolean) { 586 | val msg = "msg" 587 | val cause = new RuntimeException("cause") 588 | val arg1 = "arg1" 589 | val arg2: Integer = Integer.valueOf(1) 590 | val arg3 = "arg3" 591 | val arg4 = 4 592 | val arg4ref: AnyRef = arg4.asInstanceOf[AnyRef] 593 | val arg5 = true 594 | val arg5ref: AnyRef = arg5.asInstanceOf[AnyRef] 595 | val arg6 = 6L 596 | val arg6ref: AnyRef = arg6.asInstanceOf[AnyRef] 597 | val arg7 = new Throwable 598 | val arg7ref: AnyRef = arg7.asInstanceOf[AnyRef] 599 | val underlying: Underlying = mock[org.slf4j.Logger] 600 | when(p(underlying)).thenReturn(isEnabled) 601 | val logger: Logger = Logger(underlying) 602 | } 603 | } 604 | -------------------------------------------------------------------------------- /src/test/scala/com/typesafe/scalalogging/LoggerTakingImplicitSpec.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import org.mockito.ArgumentMatchers._ 4 | import org.mockito.Mockito._ 5 | import org.scalatestplus.mockito.MockitoSugar 6 | import org.slf4j.{ Logger => Underlying } 7 | import org.scalatest.matchers.should.Matchers 8 | import org.scalatest.wordspec.AnyWordSpec 9 | 10 | class LoggerTakingImplicitSpec extends AnyWordSpec with Matchers with Varargs with MockitoSugar { 11 | 12 | case class CorrelationId(value: String) 13 | 14 | // Error 15 | 16 | "Calling error with a message" should { 17 | 18 | "call the underlying logger's error method if the error level is enabled" in { 19 | val f = fixture(_.isErrorEnabled, isEnabled = true) 20 | import f._ 21 | logger.error(msg) 22 | verify(canLogCorrelationId).logMessage(msg, correlationId) 23 | verify(canLogCorrelationId).afterLog(correlationId) 24 | verify(underlying).error(logMsg) 25 | } 26 | 27 | "not call the underlying logger's error method if the error level is not enabled" in { 28 | val f = fixture(_.isErrorEnabled, isEnabled = false, stubCanLog = false) 29 | import f._ 30 | logger.error(msg) 31 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 32 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 33 | verify(underlying, never).error(any[String]) 34 | } 35 | } 36 | 37 | "Calling error with a message and cause" should { 38 | 39 | "call the underlying logger's error method if the error level is enabled" in { 40 | val f = fixture(_.isErrorEnabled, isEnabled = true) 41 | import f._ 42 | logger.error(msg, cause) 43 | verify(canLogCorrelationId).logMessage(msg, correlationId) 44 | verify(canLogCorrelationId).afterLog(correlationId) 45 | verify(underlying).error(logMsg, cause) 46 | } 47 | 48 | "not call the underlying logger's error method if the error level is not enabled" in { 49 | val f = fixture(_.isErrorEnabled, isEnabled = false, stubCanLog = false) 50 | import f._ 51 | logger.error(msg, cause) 52 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 53 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 54 | verify(underlying, never).error(any[String], any[Object]) 55 | } 56 | } 57 | 58 | "Calling error with a message and parameters" should { 59 | 60 | "call the underlying logger's error method if the error level is enabled" in { 61 | val f = fixture(_.isErrorEnabled, isEnabled = true) 62 | import f._ 63 | logger.error(msg, arg1) 64 | verify(underlying).error(logMsg, arg1) 65 | logger.error(msg, arg1, arg2) 66 | verify(underlying).error(logMsg, forceVarargs(arg1, arg2): _*) 67 | logger.error(msg, arg1, arg2, arg3) 68 | verify(underlying).error(logMsg, arg1, arg2, arg3) 69 | verify(canLogCorrelationId, times(3)).logMessage(msg, correlationId) 70 | verify(canLogCorrelationId, times(3)).afterLog(correlationId) 71 | } 72 | 73 | "not call the underlying logger's error method if the error level is not enabled" in { 74 | val f = fixture(_.isErrorEnabled, isEnabled = false, stubCanLog = false) 75 | import f._ 76 | logger.error(msg, arg1) 77 | verify(underlying, never).error(logMsg, arg1) 78 | logger.error(msg, arg1, arg2) 79 | verify(underlying, never).error(logMsg, forceVarargs(arg1, arg2): _*) 80 | logger.error(msg, arg1, arg2, arg3) 81 | verify(underlying, never).error(logMsg, arg1, arg2, arg3) 82 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 83 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 84 | } 85 | } 86 | 87 | // Warn 88 | 89 | "Calling warn with a message" should { 90 | 91 | "call the underlying logger's warn method if the warn level is enabled" in { 92 | val f = fixture(_.isWarnEnabled, isEnabled = true) 93 | import f._ 94 | logger.warn(msg) 95 | verify(canLogCorrelationId).logMessage(msg, correlationId) 96 | verify(canLogCorrelationId).afterLog(correlationId) 97 | verify(underlying).warn(logMsg) 98 | } 99 | 100 | "not call the underlying logger's warn method if the warn level is not enabled" in { 101 | val f = fixture(_.isWarnEnabled, isEnabled = false, stubCanLog = false) 102 | import f._ 103 | logger.warn(msg) 104 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 105 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 106 | verify(underlying, never).warn(any[String]) 107 | } 108 | } 109 | 110 | "Calling warn with a message and cause" should { 111 | 112 | "call the underlying logger's warn method if the warn level is enabled" in { 113 | val f = fixture(_.isWarnEnabled, isEnabled = true) 114 | import f._ 115 | logger.warn(msg, cause) 116 | verify(canLogCorrelationId).logMessage(msg, correlationId) 117 | verify(canLogCorrelationId).afterLog(correlationId) 118 | verify(underlying).warn(logMsg, cause) 119 | } 120 | 121 | "not call the underlying logger's warn method if the warn level is not enabled" in { 122 | val f = fixture(_.isWarnEnabled, isEnabled = false, stubCanLog = false) 123 | import f._ 124 | logger.warn(msg, cause) 125 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 126 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 127 | verify(underlying, never).warn(any[String], any[Object]) 128 | } 129 | } 130 | 131 | "Calling warn with a message and parameters" should { 132 | 133 | "call the underlying logger's warn method if the warn level is enabled" in { 134 | val f = fixture(_.isWarnEnabled, isEnabled = true) 135 | import f._ 136 | logger.warn(msg, arg1) 137 | verify(underlying).warn(logMsg, arg1) 138 | logger.warn(msg, arg1, arg2) 139 | verify(underlying).warn(logMsg, forceVarargs(arg1, arg2): _*) 140 | logger.warn(msg, arg1, arg2, arg3) 141 | verify(underlying).warn(logMsg, arg1, arg2, arg3) 142 | verify(canLogCorrelationId, times(3)).logMessage(msg, correlationId) 143 | verify(canLogCorrelationId, times(3)).afterLog(correlationId) 144 | } 145 | 146 | "not call the underlying logger's warn method if the warn level is not enabled" in { 147 | val f = fixture(_.isWarnEnabled, isEnabled = false, stubCanLog = false) 148 | import f._ 149 | logger.warn(msg, arg1) 150 | verify(underlying, never).warn(logMsg, arg1) 151 | logger.warn(msg, arg1, arg2) 152 | verify(underlying, never).warn(logMsg, forceVarargs(arg1, arg2): _*) 153 | logger.warn(msg, arg1, arg2, arg3) 154 | verify(underlying, never).warn(logMsg, arg1, arg2, arg3) 155 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 156 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 157 | } 158 | } 159 | 160 | // Info 161 | 162 | "Calling info with a message" should { 163 | 164 | "call the underlying logger's info method if the info level is enabled" in { 165 | val f = fixture(_.isInfoEnabled, isEnabled = true) 166 | import f._ 167 | logger.info(msg) 168 | verify(canLogCorrelationId).logMessage(msg, correlationId) 169 | verify(canLogCorrelationId).afterLog(correlationId) 170 | verify(underlying).info(logMsg) 171 | } 172 | 173 | "not call the underlying logger's info method if the info level is not enabled" in { 174 | val f = fixture(_.isInfoEnabled, isEnabled = false, stubCanLog = false) 175 | import f._ 176 | logger.info(msg) 177 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 178 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 179 | verify(underlying, never).info(any[String]) 180 | } 181 | } 182 | 183 | "Calling info with a message and cause" should { 184 | 185 | "call the underlying logger's info method if the info level is enabled" in { 186 | val f = fixture(_.isInfoEnabled, isEnabled = true) 187 | import f._ 188 | logger.info(msg, cause) 189 | verify(canLogCorrelationId).logMessage(msg, correlationId) 190 | verify(canLogCorrelationId).afterLog(correlationId) 191 | verify(underlying).info(logMsg, cause) 192 | } 193 | 194 | "not call the underlying logger's info method if the info level is not enabled" in { 195 | val f = fixture(_.isInfoEnabled, isEnabled = false, stubCanLog = false) 196 | import f._ 197 | logger.info(msg, cause) 198 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 199 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 200 | verify(underlying, never).info(any[String], any[Object]) 201 | } 202 | } 203 | 204 | "Calling info with a message and parameters" should { 205 | 206 | "call the underlying logger's info method if the info level is enabled" in { 207 | val f = fixture(_.isInfoEnabled, isEnabled = true) 208 | import f._ 209 | logger.info(msg, arg1) 210 | verify(underlying).info(logMsg, arg1) 211 | logger.info(msg, arg1, arg2) 212 | verify(underlying).info(logMsg, forceVarargs(arg1, arg2): _*) 213 | logger.info(msg, arg1, arg2, arg3) 214 | verify(underlying).info(logMsg, arg1, arg2, arg3) 215 | verify(canLogCorrelationId, times(3)).logMessage(msg, correlationId) 216 | verify(canLogCorrelationId, times(3)).afterLog(correlationId) 217 | } 218 | 219 | "not call the underlying logger's info method if the info level is not enabled" in { 220 | val f = fixture(_.isInfoEnabled, isEnabled = false, stubCanLog = false) 221 | import f._ 222 | logger.info(msg, arg1) 223 | verify(underlying, never).info(logMsg, arg1) 224 | logger.info(msg, arg1, arg2) 225 | verify(underlying, never).info(logMsg, forceVarargs(arg1, arg2): _*) 226 | logger.info(msg, arg1, arg2, arg3) 227 | verify(underlying, never).info(logMsg, arg1, arg2, arg3) 228 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 229 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 230 | } 231 | } 232 | 233 | // Debug 234 | 235 | "Calling debug with a message" should { 236 | 237 | "call the underlying logger's debug method if the debug level is enabled" in { 238 | val f = fixture(_.isDebugEnabled, isEnabled = true) 239 | import f._ 240 | logger.debug(msg) 241 | verify(canLogCorrelationId).logMessage(msg, correlationId) 242 | verify(canLogCorrelationId).afterLog(correlationId) 243 | verify(underlying).debug(logMsg) 244 | } 245 | 246 | "not call the underlying logger's debug method if the debug level is not enabled" in { 247 | val f = fixture(_.isDebugEnabled, isEnabled = false, stubCanLog = false) 248 | import f._ 249 | logger.debug(msg) 250 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 251 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 252 | verify(underlying, never).debug(any[String]) 253 | } 254 | } 255 | 256 | "Calling debug with a message and cause" should { 257 | 258 | "call the underlying logger's debug method if the debug level is enabled" in { 259 | val f = fixture(_.isDebugEnabled, isEnabled = true) 260 | import f._ 261 | logger.debug(msg, cause) 262 | verify(canLogCorrelationId).logMessage(msg, correlationId) 263 | verify(canLogCorrelationId).afterLog(correlationId) 264 | verify(underlying).debug(logMsg, cause) 265 | } 266 | 267 | "not call the underlying logger's debug method if the debug level is not enabled" in { 268 | val f = fixture(_.isDebugEnabled, isEnabled = false, stubCanLog = false) 269 | import f._ 270 | logger.debug(msg, cause) 271 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 272 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 273 | verify(underlying, never).debug(any[String], any[Object]) 274 | } 275 | } 276 | 277 | "Calling debug with a message and parameters" should { 278 | 279 | "call the underlying logger's debug method if the debug level is enabled" in { 280 | val f = fixture(_.isDebugEnabled, isEnabled = true) 281 | import f._ 282 | logger.debug(msg, arg1) 283 | verify(underlying).debug(logMsg, arg1) 284 | logger.debug(msg, arg1, arg2) 285 | verify(underlying).debug(logMsg, forceVarargs(arg1, arg2): _*) 286 | logger.debug(msg, arg1, arg2, arg3) 287 | verify(underlying).debug(logMsg, arg1, arg2, arg3) 288 | verify(canLogCorrelationId, times(3)).logMessage(msg, correlationId) 289 | verify(canLogCorrelationId, times(3)).afterLog(correlationId) 290 | } 291 | 292 | "not call the underlying logger's debug method if the debug level is not enabled" in { 293 | val f = fixture(_.isDebugEnabled, isEnabled = false, stubCanLog = false) 294 | import f._ 295 | logger.debug(msg, arg1) 296 | verify(underlying, never).debug(logMsg, arg1) 297 | logger.debug(msg, arg1, arg2) 298 | verify(underlying, never).debug(logMsg, forceVarargs(arg1, arg2): _*) 299 | logger.debug(msg, arg1, arg2, arg3) 300 | verify(underlying, never).debug(logMsg, arg1, arg2, arg3) 301 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 302 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 303 | } 304 | } 305 | 306 | // Trace 307 | 308 | "Calling trace with a message" should { 309 | 310 | "call the underlying logger's trace method if the trace level is enabled" in { 311 | val f = fixture(_.isTraceEnabled, isEnabled = true) 312 | import f._ 313 | logger.trace(msg) 314 | verify(canLogCorrelationId).logMessage(msg, correlationId) 315 | verify(canLogCorrelationId).afterLog(correlationId) 316 | verify(underlying).trace(logMsg) 317 | } 318 | 319 | "not call the underlying logger's trace method if the trace level is not enabled" in { 320 | val f = fixture(_.isTraceEnabled, isEnabled = false, stubCanLog = false) 321 | import f._ 322 | logger.trace(msg) 323 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 324 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 325 | verify(underlying, never).trace(any[String]) 326 | } 327 | } 328 | 329 | "Calling trace with a message and cause" should { 330 | 331 | "call the underlying logger's trace method if the trace level is enabled" in { 332 | val f = fixture(_.isTraceEnabled, isEnabled = true) 333 | import f._ 334 | logger.trace(msg, cause) 335 | verify(canLogCorrelationId).logMessage(msg, correlationId) 336 | verify(canLogCorrelationId).afterLog(correlationId) 337 | verify(underlying).trace(logMsg, cause) 338 | } 339 | 340 | "not call the underlying logger's trace method if the trace level is not enabled" in { 341 | val f = fixture(_.isTraceEnabled, isEnabled = false, stubCanLog = false) 342 | import f._ 343 | logger.trace(msg, cause) 344 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 345 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 346 | verify(underlying, never).trace(any[String], any[Object]) 347 | } 348 | } 349 | 350 | "Calling trace with a message and parameters" should { 351 | 352 | "call the underlying logger's trace method if the trace level is enabled" in { 353 | val f = fixture(_.isTraceEnabled, isEnabled = true) 354 | import f._ 355 | logger.trace(msg, arg1) 356 | verify(underlying).trace(logMsg, arg1) 357 | logger.trace(msg, arg1, arg2) 358 | verify(underlying).trace(logMsg, forceVarargs(arg1, arg2): _*) 359 | logger.trace(msg, arg1, arg2, arg3) 360 | verify(underlying).trace(logMsg, arg1, arg2, arg3) 361 | verify(canLogCorrelationId, times(3)).logMessage(msg, correlationId) 362 | verify(canLogCorrelationId, times(3)).afterLog(correlationId) 363 | } 364 | 365 | "not call the underlying logger's trace method if the trace level is not enabled" in { 366 | val f = fixture(_.isTraceEnabled, isEnabled = false, stubCanLog = false) 367 | import f._ 368 | logger.trace(msg, arg1) 369 | verify(underlying, never).trace(logMsg, arg1) 370 | logger.trace(msg, arg1, arg2) 371 | verify(underlying, never).trace(logMsg, forceVarargs(arg1, arg2): _*) 372 | logger.trace(msg, arg1, arg2, arg3) 373 | verify(underlying, never).trace(logMsg, arg1, arg2, arg3) 374 | verify(canLogCorrelationId, never).logMessage(any[String], any[CorrelationId]) 375 | verify(canLogCorrelationId, never).afterLog(any[CorrelationId]) 376 | } 377 | } 378 | 379 | "Calling getContext" should { 380 | "call getContext on the underlying logger's CanLog" in { 381 | val f = fixture(_.isErrorEnabled, isEnabled = true) 382 | import f._ 383 | logger.canLogEv.getContext() 384 | verify(canLogCorrelationId).getContext() 385 | } 386 | } 387 | 388 | private def fixture(p: Underlying => Boolean, isEnabled: Boolean, stubCanLog: Boolean = true) = new LoggerF(p, isEnabled, stubCanLog) 389 | private class LoggerF(p: Underlying => Boolean, isEnabled: Boolean, stubCanLog: Boolean = true) { 390 | implicit val correlationId: CorrelationId = CorrelationId("corrId") 391 | implicit val canLogCorrelationId: CanLog[CorrelationId] = mock[CanLog[CorrelationId]] 392 | val msg = "msg" 393 | val cause = new RuntimeException("cause") 394 | val arg1 = "arg1" 395 | val arg2: Integer = Integer.valueOf(1) 396 | val arg3 = "arg3" 397 | val logMsg = "corrId - msg" 398 | val underlying: Underlying = mock[org.slf4j.Logger] 399 | when(p(underlying)).thenReturn(isEnabled) 400 | if (stubCanLog) when(canLogCorrelationId.logMessage(any[String], any[CorrelationId])).thenReturn(logMsg) 401 | val logger: LoggerTakingImplicit[CorrelationId] = Logger.takingImplicit[CorrelationId](underlying) 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /src/test/scala/com/typesafe/scalalogging/LoggerWithMarkerSpec.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import java.util.NoSuchElementException 4 | import org.slf4j.{ Logger => Underlying } 5 | import org.slf4j.Marker 6 | import org.scalatest.matchers.should.Matchers 7 | import org.scalatest.wordspec.AnyWordSpec 8 | import org.scalatestplus.mockito.MockitoSugar 9 | import org.mockito.ArgumentMatchers._ 10 | import org.mockito.Mockito._ 11 | 12 | object DummyMarker extends Marker { 13 | def add(childMarker: Marker): Unit = {} 14 | def contains(childName: String): Boolean = false 15 | def contains(child: Marker): Boolean = false 16 | def getName: String = "DummyMarker" 17 | def hasChildren: Boolean = false 18 | def hasReferences: Boolean = false 19 | def iterator(): java.util.Iterator[Marker] = new java.util.Iterator[Marker] { 20 | def hasNext: Boolean = false 21 | def next(): Marker = throw new NoSuchElementException() 22 | override def remove(): Unit = throw new NoSuchElementException() 23 | } 24 | def remove(child: Marker): Boolean = false 25 | } 26 | 27 | class LoggerWithMarkerSpec extends AnyWordSpec with Matchers with Varargs with MockitoSugar { 28 | 29 | // Error 30 | 31 | "Calling error with a marker and a message" should { 32 | 33 | "call the underlying logger's error method if the error level is enabled" in { 34 | val f = fixture(_.isErrorEnabled, isEnabled = true) 35 | import f._ 36 | logger.error(marker, msg) 37 | verify(underlying).error(marker, msg) 38 | } 39 | 40 | "not call the underlying logger's error method if the error level is not enabled" in { 41 | val f = fixture(_.isErrorEnabled, isEnabled = false) 42 | import f._ 43 | logger.error(marker, msg) 44 | verify(underlying, never).error(refEq(DummyMarker), any[String]) 45 | } 46 | 47 | "call the underlying logger's error method if the error level is enabled and string is interpolated" in { 48 | val f = fixture(_.isErrorEnabled, isEnabled = true) 49 | import f._ 50 | logger.error(marker, s"msg $arg1 $arg2 $arg3") 51 | verify(underlying).error(marker, "msg {} {} {}", arg1, arg2, arg3) 52 | } 53 | } 54 | 55 | "Calling error with a marker and a message and cause" should { 56 | 57 | "call the underlying logger's error method if the error level is enabled" in { 58 | val f = fixture(_.isErrorEnabled, isEnabled = true) 59 | import f._ 60 | logger.error(marker, msg, cause) 61 | verify(underlying).error(marker, msg, cause) 62 | } 63 | 64 | "not call the underlying logger's error method if the error level is not enabled" in { 65 | val f = fixture(_.isErrorEnabled, isEnabled = false) 66 | import f._ 67 | logger.error(marker, msg, cause) 68 | verify(underlying, never).error(refEq(DummyMarker), any[String], any[Object]) 69 | } 70 | } 71 | 72 | "Calling error with a marker and a message and parameters" should { 73 | 74 | "call the underlying logger's error method if the error level is enabled" in { 75 | val f = fixture(_.isErrorEnabled, isEnabled = true) 76 | import f._ 77 | logger.error(marker, msg, arg1) 78 | verify(underlying).error(marker, msg, arg1) 79 | logger.error(marker, msg, arg1, arg2) 80 | verify(underlying).error(marker, msg, forceVarargs(arg1, arg2): _*) 81 | logger.error(marker, msg, arg1, arg2, arg3) 82 | verify(underlying).error(marker, msg, arg1, arg2, arg3) 83 | } 84 | 85 | "not call the underlying logger's error method if the error level is not enabled" in { 86 | val f = fixture(_.isErrorEnabled, isEnabled = false) 87 | import f._ 88 | logger.error(marker, msg, arg1) 89 | verify(underlying, never).error(marker, msg, arg1) 90 | logger.error(marker, msg, arg1, arg2) 91 | verify(underlying, never).error(marker, msg, forceVarargs(arg1, arg2): _*) 92 | logger.error(marker, msg, arg1, arg2, arg3) 93 | verify(underlying, never).error(marker, msg, arg1, arg2, arg3) 94 | } 95 | } 96 | 97 | // Warn 98 | 99 | "Calling warn with a marker and a message" should { 100 | 101 | "call the underlying logger's warn method if the warn level is enabled" in { 102 | val f = fixture(_.isWarnEnabled, isEnabled = true) 103 | import f._ 104 | logger.warn(marker, msg) 105 | verify(underlying).warn(marker, msg) 106 | } 107 | 108 | "not call the underlying logger's warn method if the warn level is not enabled" in { 109 | val f = fixture(_.isWarnEnabled, isEnabled = false) 110 | import f._ 111 | logger.warn(marker, msg) 112 | verify(underlying, never).warn(refEq(DummyMarker), any[String]) 113 | } 114 | 115 | "call the underlying logger's warn method if the warn level is enabled and string is interpolated" in { 116 | val f = fixture(_.isWarnEnabled, isEnabled = true) 117 | import f._ 118 | logger.warn(marker, s"msg $arg1 $arg2 $arg3") 119 | verify(underlying).warn(marker, "msg {} {} {}", arg1, arg2, arg3) 120 | } 121 | } 122 | 123 | "Calling warn with a marker and a message and cause" should { 124 | 125 | "call the underlying logger's warn method if the warn level is enabled" in { 126 | val f = fixture(_.isWarnEnabled, isEnabled = true) 127 | import f._ 128 | logger.warn(marker, msg, cause) 129 | verify(underlying).warn(marker, msg, cause) 130 | } 131 | 132 | "not call the underlying logger's warn method if the warn level is not enabled" in { 133 | val f = fixture(_.isWarnEnabled, isEnabled = false) 134 | import f._ 135 | logger.warn(marker, msg, cause) 136 | verify(underlying, never).warn(refEq(DummyMarker), any[String], any[Object]) 137 | } 138 | } 139 | 140 | "Calling warn with a marker and a message and parameters" should { 141 | 142 | "call the underlying logger's warn method if the warn level is enabled" in { 143 | val f = fixture(_.isWarnEnabled, isEnabled = true) 144 | import f._ 145 | logger.warn(marker, msg, arg1) 146 | verify(underlying).warn(marker, msg, arg1) 147 | logger.warn(marker, msg, arg1, arg2) 148 | verify(underlying).warn(marker, msg, forceVarargs(arg1, arg2): _*) 149 | logger.warn(marker, msg, arg1, arg2, arg3) 150 | verify(underlying).warn(marker, msg, arg1, arg2, arg3) 151 | } 152 | 153 | "not call the underlying logger's warn method if the warn level is not enabled" in { 154 | val f = fixture(_.isWarnEnabled, isEnabled = false) 155 | import f._ 156 | logger.warn(marker, msg, arg1) 157 | verify(underlying, never).warn(marker, msg, arg1) 158 | logger.warn(marker, msg, arg1, arg2) 159 | verify(underlying, never).warn(marker, msg, forceVarargs(arg1, arg2): _*) 160 | logger.warn(marker, msg, arg1, arg2, arg3) 161 | verify(underlying, never).warn(marker, msg, arg1, arg2, arg3) 162 | } 163 | } 164 | 165 | // Info 166 | 167 | "Calling info with a marker and a message" should { 168 | 169 | "call the underlying logger's info method if the info level is enabled" in { 170 | val f = fixture(_.isInfoEnabled, isEnabled = true) 171 | import f._ 172 | logger.info(marker, msg) 173 | verify(underlying).info(marker, msg) 174 | } 175 | 176 | "not call the underlying logger's info method if the info level is not enabled" in { 177 | val f = fixture(_.isInfoEnabled, isEnabled = false) 178 | import f._ 179 | logger.info(marker, msg) 180 | verify(underlying, never).info(refEq(DummyMarker), any[String]) 181 | } 182 | 183 | "call the underlying logger's info method if the info level is enabled and string is interpolated" in { 184 | val f = fixture(_.isInfoEnabled, isEnabled = true) 185 | import f._ 186 | logger.info(marker, s"msg $arg1 $arg2 $arg3") 187 | verify(underlying).info(marker, "msg {} {} {}", arg1, arg2, arg3) 188 | } 189 | } 190 | 191 | "Calling info with a marker and a message and cause" should { 192 | 193 | "call the underlying logger's info method if the info level is enabled" in { 194 | val f = fixture(_.isInfoEnabled, isEnabled = true) 195 | import f._ 196 | logger.info(marker, msg, cause) 197 | verify(underlying).info(marker, msg, cause) 198 | } 199 | 200 | "not call the underlying logger's info method if the info level is not enabled" in { 201 | val f = fixture(_.isInfoEnabled, isEnabled = false) 202 | import f._ 203 | logger.info(marker, msg, cause) 204 | verify(underlying, never).info(refEq(DummyMarker), any[String], any[Object]) 205 | } 206 | } 207 | 208 | "Calling info with a marker and a message and parameters" should { 209 | 210 | "call the underlying logger's info method if the info level is enabled" in { 211 | val f = fixture(_.isInfoEnabled, isEnabled = true) 212 | import f._ 213 | logger.info(marker, msg, arg1) 214 | verify(underlying).info(marker, msg, arg1) 215 | logger.info(marker, msg, arg1, arg2) 216 | verify(underlying).info(marker, msg, forceVarargs(arg1, arg2): _*) 217 | logger.info(marker, msg, arg1, arg2, arg3) 218 | verify(underlying).info(marker, msg, arg1, arg2, arg3) 219 | } 220 | 221 | "not call the underlying logger's info method if the info level is not enabled" in { 222 | val f = fixture(_.isInfoEnabled, isEnabled = false) 223 | import f._ 224 | logger.info(marker, msg, arg1) 225 | verify(underlying, never).info(marker, msg, arg1) 226 | logger.info(marker, msg, arg1, arg2) 227 | verify(underlying, never).info(marker, msg, forceVarargs(arg1, arg2): _*) 228 | logger.info(marker, msg, arg1, arg2, arg3) 229 | verify(underlying, never).info(marker, msg, arg1, arg2, arg3) 230 | } 231 | } 232 | 233 | // Debug 234 | 235 | "Calling debug with a marker and a message" should { 236 | 237 | "call the underlying logger's debug method if the debug level is enabled" in { 238 | val f = fixture(_.isDebugEnabled, isEnabled = true) 239 | import f._ 240 | logger.debug(marker, msg) 241 | verify(underlying).debug(marker, msg) 242 | } 243 | 244 | "not call the underlying logger's debug method if the debug level is not enabled" in { 245 | val f = fixture(_.isDebugEnabled, isEnabled = false) 246 | import f._ 247 | logger.debug(marker, msg) 248 | verify(underlying, never).debug(refEq(DummyMarker), any[String]) 249 | } 250 | 251 | "call the underlying logger's debug method if the debug level is enabled and string is interpolated" in { 252 | val f = fixture(_.isDebugEnabled, isEnabled = true) 253 | import f._ 254 | logger.debug(marker, s"msg $arg1 $arg2 $arg3") 255 | verify(underlying).debug(marker, "msg {} {} {}", arg1, arg2, arg3) 256 | } 257 | } 258 | 259 | "Calling debug with a marker and a message and cause" should { 260 | 261 | "call the underlying logger's debug method if the debug level is enabled" in { 262 | val f = fixture(_.isDebugEnabled, isEnabled = true) 263 | import f._ 264 | logger.debug(marker, msg, cause) 265 | verify(underlying).debug(marker, msg, cause) 266 | } 267 | 268 | "not call the underlying logger's debug method if the debug level is not enabled" in { 269 | val f = fixture(_.isDebugEnabled, isEnabled = false) 270 | import f._ 271 | logger.debug(marker, msg, cause) 272 | verify(underlying, never).debug(refEq(DummyMarker), any[String], any[Object]) 273 | } 274 | } 275 | 276 | "Calling debug with a marker and a message and parameters" should { 277 | 278 | "call the underlying logger's debug method if the debug level is enabled" in { 279 | val f = fixture(_.isDebugEnabled, isEnabled = true) 280 | import f._ 281 | logger.debug(marker, msg, arg1) 282 | verify(underlying).debug(marker, msg, arg1) 283 | logger.debug(marker, msg, arg1, arg2) 284 | verify(underlying).debug(marker, msg, forceVarargs(arg1, arg2): _*) 285 | logger.debug(marker, msg, arg1, arg2, arg3) 286 | verify(underlying).debug(marker, msg, arg1, arg2, arg3) 287 | } 288 | 289 | "not call the underlying logger's debug method if the debug level is not enabled" in { 290 | val f = fixture(_.isDebugEnabled, isEnabled = false) 291 | import f._ 292 | logger.debug(marker, msg, arg1) 293 | verify(underlying, never).debug(marker, msg, arg1) 294 | logger.debug(marker, msg, arg1, arg2) 295 | verify(underlying, never).debug(marker, msg, forceVarargs(arg1, arg2): _*) 296 | logger.debug(marker, msg, arg1, arg2, arg3) 297 | verify(underlying, never).debug(marker, msg, arg1, arg2, arg3) 298 | } 299 | } 300 | 301 | // Trace 302 | 303 | "Calling trace with a marker and a message" should { 304 | 305 | "call the underlying logger's trace method if the trace level is enabled" in { 306 | val f = fixture(_.isTraceEnabled, isEnabled = true) 307 | import f._ 308 | logger.trace(marker, msg) 309 | verify(underlying).trace(marker, msg) 310 | } 311 | 312 | "not call the underlying logger's trace method if the trace level is not enabled" in { 313 | val f = fixture(_.isTraceEnabled, isEnabled = false) 314 | import f._ 315 | logger.trace(marker, msg) 316 | verify(underlying, never).trace(refEq(DummyMarker), any[String]) 317 | } 318 | 319 | "call the underlying logger's trace method if the trace level is enabled and string is interpolated" in { 320 | val f = fixture(_.isTraceEnabled, isEnabled = true) 321 | import f._ 322 | logger.trace(marker, s"msg $arg1 $arg2 $arg3") 323 | verify(underlying).trace(marker, "msg {} {} {}", arg1, arg2, arg3) 324 | } 325 | } 326 | 327 | "Calling trace with a marker and a message and cause" should { 328 | 329 | "call the underlying logger's trace method if the trace level is enabled" in { 330 | val f = fixture(_.isTraceEnabled, isEnabled = true) 331 | import f._ 332 | logger.trace(marker, msg, cause) 333 | verify(underlying).trace(marker, msg, cause) 334 | } 335 | 336 | "not call the underlying logger's trace method if the trace level is not enabled" in { 337 | val f = fixture(_.isTraceEnabled, isEnabled = false) 338 | import f._ 339 | logger.trace(marker, msg, cause) 340 | verify(underlying, never).trace(refEq(DummyMarker), any[String], any[Object]) 341 | } 342 | } 343 | 344 | "Calling trace with a marker and a message and parameters" should { 345 | 346 | "call the underlying logger's trace method if the trace level is enabled" in { 347 | val f = fixture(_.isTraceEnabled, isEnabled = true) 348 | import f._ 349 | logger.trace(marker, msg, arg1) 350 | verify(underlying).trace(marker, msg, arg1) 351 | logger.trace(marker, msg, arg1, arg2) 352 | verify(underlying).trace(marker, msg, forceVarargs(arg1, arg2): _*) 353 | logger.trace(marker, msg, arg1, arg2, arg3) 354 | verify(underlying).trace(marker, msg, arg1, arg2, arg3) 355 | } 356 | 357 | "not call the underlying logger's trace method if the trace level is not enabled" in { 358 | val f = fixture(_.isTraceEnabled, isEnabled = false) 359 | import f._ 360 | logger.trace(marker, msg, arg1) 361 | verify(underlying, never).trace(marker, msg, arg1) 362 | logger.trace(marker, msg, arg1, arg2) 363 | verify(underlying, never).trace(marker, msg, forceVarargs(arg1, arg2): _*) 364 | logger.trace(marker, msg, arg1, arg2, arg3) 365 | verify(underlying, never).trace(marker, msg, arg1, arg2, arg3) 366 | } 367 | } 368 | 369 | private def fixture(p: Underlying => Marker => Boolean, isEnabled: Boolean) = new LoggerF(p, isEnabled) 370 | private class LoggerF(p: Underlying => Marker => Boolean, isEnabled: Boolean) { 371 | val marker: DummyMarker.type = DummyMarker 372 | val msg = "msg" 373 | val cause = new RuntimeException("cause") 374 | val arg1 = "arg1" 375 | val arg2: Integer = Integer.valueOf(1) 376 | val arg3 = "arg3" 377 | val underlying: Underlying = mock[org.slf4j.Logger] 378 | 379 | when(p(underlying)(marker)).thenReturn(isEnabled) 380 | 381 | val logger: Logger = Logger(underlying) 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /src/test/scala/com/typesafe/scalalogging/LoggerWithTaggedArgsSpec.scala: -------------------------------------------------------------------------------- 1 | package com.typesafe.scalalogging 2 | 3 | import com.typesafe.scalalogging.tag.@@ 4 | import org.slf4j.{ Logger => Underlying } 5 | import org.scalatest.matchers.should.Matchers 6 | import org.scalatest.wordspec.AnyWordSpec 7 | import org.scalatestplus.mockito.MockitoSugar 8 | import org.mockito.ArgumentMatchers._ 9 | import org.mockito.Mockito._ 10 | 11 | object tag { 12 | trait Tagged[U] 13 | type @@[+T, U] = T with Tagged[U] 14 | 15 | def taggedString[T](s: String): String @@ T = s.asInstanceOf[String @@ T] 16 | def taggedInteger[T](i: Integer): Integer @@ T = i.asInstanceOf[Integer @@ T] 17 | def taggedBoolean[T](b: Boolean): Boolean @@ T = b.asInstanceOf[Boolean @@ T] 18 | } 19 | 20 | class LoggerWithTaggedArgsSpec extends AnyWordSpec with Matchers with Varargs with MockitoSugar { 21 | 22 | trait Tag 23 | 24 | "Calling error with tagged args" should { 25 | 26 | "not throw ClassCastException when varargs are passed" in { 27 | val f = fixture(_.isErrorEnabled) 28 | import f._ 29 | noException shouldBe thrownBy { 30 | logger.error("This should not throw: {}, {} - {}", arg1, arg2, arg3) 31 | } 32 | verify(underlying).error(any[String], any, any, any) 33 | } 34 | 35 | "not throw ClassCastException when interpolated message is passed" in { 36 | val f = fixture(_.isErrorEnabled) 37 | import f._ 38 | noException shouldBe thrownBy { 39 | logger.error(s"This should not throw: $arg1, $arg2, $arg3") 40 | } 41 | verify(underlying).error(any[String], any, any, any) 42 | } 43 | } 44 | 45 | "Calling trace with tagged args" should { 46 | 47 | "not throw ClassCastException when varargs are passed" in { 48 | val f = fixture(_.isTraceEnabled) 49 | import f._ 50 | noException shouldBe thrownBy { 51 | logger.trace("This should not throw: {}, {} - {}", arg1, arg2, arg3) 52 | } 53 | verify(underlying).trace(any[String], any, any, any) 54 | } 55 | 56 | "not throw ClassCastException when interpolated message is passed" in { 57 | val f = fixture(_.isTraceEnabled) 58 | import f._ 59 | noException shouldBe thrownBy { 60 | logger.trace(s"This should not throw: $arg1, $arg2, $arg3") 61 | } 62 | verify(underlying).trace(any[String], any, any, any) 63 | } 64 | } 65 | 66 | "Calling debug with tagged args" should { 67 | 68 | "not throw ClassCastException when varargs are passed" in { 69 | val f = fixture(_.isDebugEnabled) 70 | import f._ 71 | noException shouldBe thrownBy { 72 | logger.debug("This should not throw: {}, {} - {}", arg1, arg2, arg3) 73 | } 74 | verify(underlying).debug(any[String], any, any, any) 75 | } 76 | 77 | "not throw ClassCastException when interpolated message is passed" in { 78 | val f = fixture(_.isDebugEnabled) 79 | import f._ 80 | noException shouldBe thrownBy { 81 | logger.debug(s"This should not throw: $arg1, $arg2, $arg3") 82 | } 83 | verify(underlying).debug(any[String], any, any, any) 84 | } 85 | } 86 | 87 | "Calling info with tagged args" should { 88 | 89 | "not throw ClassCastException when varargs are passed" in { 90 | val f = fixture(_.isInfoEnabled) 91 | import f._ 92 | noException shouldBe thrownBy { 93 | logger.info("This should not throw: {}, {} - {}", arg1, arg2, arg3) 94 | } 95 | verify(underlying).info(any[String], any, any, any) 96 | } 97 | 98 | "not throw ClassCastException when interpolated message is passed" in { 99 | val f = fixture(_.isInfoEnabled) 100 | import f._ 101 | noException shouldBe thrownBy { 102 | logger.info(s"This should not throw: $arg1, $arg2, $arg3") 103 | } 104 | verify(underlying).info(any[String], any, any, any) 105 | } 106 | } 107 | 108 | private def fixture(p: Underlying => Boolean, isEnabled: Boolean = true) = new LoggerF(p, isEnabled) 109 | private class LoggerF(p: Underlying => Boolean, isEnabled: Boolean = true) { 110 | val arg1: String @@ Tag = tag.taggedString[Tag]("arg1") 111 | val arg2: Integer @@ Tag = tag.taggedInteger[Tag](Integer.valueOf(1)) 112 | val arg3: Boolean @@ Boolean = tag.taggedBoolean[Boolean](true) 113 | val underlying: Underlying = mock[org.slf4j.Logger] 114 | when(p(underlying)).thenReturn(isEnabled) 115 | val logger: Logger = Logger(underlying) 116 | } 117 | } 118 | --------------------------------------------------------------------------------