Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Slf4j2 bridge #577

Merged
merged 14 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ inThisBuild(
)
)

val ZioVersion = "2.0.6"
val scalaJavaTimeVersion = "2.3.0"
val slf4jVersion = "1.7.36"
val logbackVersion = "1.2.11"
val ZioVersion = "2.0.6"
val slf4jVersion = "1.7.36"
val slf4j2Version = "2.0.6"
val logbackVersion = "1.2.11"

addCommandAlias("fix", "; all compile:scalafix test:scalafix; all scalafmtSbt scalafmtAll")
addCommandAlias("check", "; scalafmtSbtCheck; scalafmtCheckAll; compile:scalafix --check; test:scalafix --check")
Expand All @@ -37,7 +37,7 @@ addCommandAlias(

addCommandAlias(
"testJVM",
";coreJVM/test;slf4j/test;jpl/test;slf4jBridge/test"
";coreJVM/test;slf4j/test;jpl/test;slf4jBridge/test;slf4j2Bridge/test"
)

addCommandAlias(
Expand All @@ -55,7 +55,7 @@ lazy val root = project
.settings(
publish / skip := true
)
.aggregate(coreJVM, coreJS, slf4j, slf4jBridge, jpl, benchmarks, examples, docs)
.aggregate(coreJVM, coreJS, slf4j, slf4jBridge, slf4j2Bridge, jpl, benchmarks, examples, docs)

lazy val core = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Full)
Expand Down Expand Up @@ -113,6 +113,27 @@ lazy val slf4jBridge = project
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)

lazy val slf4j2Bridge = project
.in(file("slf4j2-bridge"))
.dependsOn(coreJVM)
.settings(stdSettings("zio-logging-slf4j2-bridge", "9"))
.settings(mimaSettings(failOnProblem = true))
.settings(
compileOrder := CompileOrder.JavaThenScala,
javacOptions := jpmsOverwriteModulePath((Compile / dependencyClasspath).value.map(_.data))(javacOptions.value),
javaOptions := jpmsOverwriteModulePath((Compile / dependencyClasspath).value.map(_.data))(javaOptions.value),
Compile / packageDoc / publishArtifact := false // module-info.java compilation issue
)
.settings(
libraryDependencies ++= Seq(
"org.slf4j" % "slf4j-api" % slf4j2Version,
"org.scala-lang.modules" %% "scala-collection-compat" % "2.8.1",
"dev.zio" %%% "zio-test" % ZioVersion % Test,
"dev.zio" %%% "zio-test-sbt" % ZioVersion % Test
),
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)

lazy val jpl = project
.in(file("jpl"))
.dependsOn(coreJVM)
Expand Down
5 changes: 4 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ libraryDependencies += "dev.zio" %% "zio-logging-jpl" % "@VERSION@"
// SLF4j integration
libraryDependencies += "dev.zio" %% "zio-logging-slf4j" % "@VERSION@"

// Using ZIO Logging for SLF4j loggers, usually third-party non-ZIO libraries
// Using ZIO Logging for SLF4j v1 loggers, usually third-party non-ZIO libraries
libraryDependencies += "dev.zio" %% "zio-logging-slf4j-bridge" % "@VERSION@"

// Using ZIO Logging for SLF4j v2 loggers, usually third-party non-ZIO libraries
libraryDependencies += "dev.zio" %% "zio-logging-slf4j2-bridge" % "@VERSION@"
```

## Example
Expand Down
13 changes: 10 additions & 3 deletions docs/slf4j-bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@ title: "SLF4J bridge"
---

It is possible to use `zio-logging` for SLF4J loggers, usually third-party non-ZIO libraries. To do so, import
the `zio-logging-slf4j-bridge` module:

* the `zio-logging-slf4j-bridge` module for SLF4J v1:

```scala
libraryDependencies += "dev.zio" %% "zio-logging-slf4j-bridge" % "@VERSION@"
```

* the `zio-logging-slf4j2-bridge` module for [SLF4J v2](https://www.slf4j.org/faq.html#changesInVersion200) (using JDK9+ module system ([JPMS](http://openjdk.java.net/projects/jigsaw/spec/)))

```scala
libraryDependencies += "dev.zio" %% "zio-logging-slf4j2-bridge" % "@VERSION@"
```


and use the `Slf4jBridge.initialize` layer when setting up logging:

```scala
Expand Down Expand Up @@ -67,13 +75,12 @@ ZIO logging. Enabling both causes circular logging and makes no sense.
```scala
package zio.logging.slf4j.bridge

import org.slf4j.{ Logger, LoggerFactory }
import zio.logging.{ LogFilter, LogFormat, LoggerNameExtractor, consoleJson }
import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer }

object Slf4jBridgeExampleApp extends ZIOAppDefault {

private val slf4jLogger: Logger = LoggerFactory.getLogger("SLF4J-LOGGER")
private val slf4jLogger = org.slf4j.LoggerFactory.getLogger("SLF4J-LOGGER")

private val loggerName = LoggerNameExtractor.annotationOrTrace(Slf4jBridge.loggerNameAnnotationKey)

Expand Down
8 changes: 8 additions & 0 deletions project/BuildHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,14 @@ object BuildHelper {
Compile / doc / sources := Seq.empty
)

def jpmsOverwriteModulePath(modulePaths: Seq[File])(options: Seq[String]): Seq[String] = {
val modPathString = modulePaths.map(_.getAbsolutePath).mkString(java.io.File.pathSeparator)
val option = "--module-path"
val index = options.indexWhere(_ == option)
if (index == -1) options ++ List(option, modPathString)
else options.patch(index + 1, List(modPathString), 1)
}

val scalaReflectTestSettings: List[Setting[_]] = List(
libraryDependencies ++= {
if (scalaVersion.value == Scala3)
Expand Down
2 changes: 1 addition & 1 deletion project/MimaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import sbt.Keys.{ name, organization }
import sbt._

object MimaSettings {
lazy val bincompatVersionToCompare = "2.1.4"
lazy val bincompatVersionToCompare = "2.1.7"

def mimaSettings(failOnProblem: Boolean) =
Seq(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package zio.logging.slf4j.bridge

import org.slf4j.{ Logger, LoggerFactory }
import zio.logging.{ LogFilter, LogFormat, LoggerNameExtractor, consoleJson }
import zio.{ ExitCode, LogLevel, Runtime, Scope, ZIO, ZIOAppArgs, ZIOAppDefault, ZLayer }

object Slf4jBridgeExampleApp extends ZIOAppDefault {

private val slf4jLogger: Logger = LoggerFactory.getLogger("SLF4J-LOGGER")
private val slf4jLogger = org.slf4j.LoggerFactory.getLogger("SLF4J-LOGGER")

private val loggerName = LoggerNameExtractor.annotationOrTrace(Slf4jBridge.loggerNameAnnotationKey)

Expand Down
4 changes: 4 additions & 0 deletions slf4j2-bridge/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module org.slf4j.zio {
requires org.slf4j;
provides org.slf4j.spi.SLF4JServiceProvider with zio.logging.slf4j.bridge.ZioSLF4JServiceProvider;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2019-2023 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio.logging.slf4j.bridge;

import org.slf4j.Marker;
import org.slf4j.event.Level;
import org.slf4j.helpers.AbstractLogger;

final class Logger extends AbstractLogger {

private LoggerFactory factory;

Logger(String name, LoggerFactory factory) {
this.name = name;
this.factory = factory;
}

@Override
protected String getFullyQualifiedCallerName() {
return null;
}

@Override
protected void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
factory.log(name, level, marker, messagePattern, arguments, throwable);
}

@Override
public boolean isTraceEnabled() {
return true;
}

@Override
public boolean isTraceEnabled(Marker marker) {
return true;
}

@Override
public boolean isDebugEnabled() {
return true;
}

@Override
public boolean isDebugEnabled(Marker marker) {
return true;
}

@Override
public boolean isInfoEnabled() {
return true;
}

@Override
public boolean isInfoEnabled(Marker marker) {
return true;
}

@Override
public boolean isWarnEnabled() {
return true;
}

@Override
public boolean isWarnEnabled(Marker marker) {
return true;
}

@Override
public boolean isErrorEnabled() {
return true;
}

@Override
public boolean isErrorEnabled(Marker marker) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2019-2023 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio.logging.slf4j.bridge;

import org.slf4j.ILoggerFactory;
import org.slf4j.Marker;
import org.slf4j.event.Level;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

final class LoggerFactory implements ILoggerFactory {

private Map<String, Logger> loggers = new ConcurrentHashMap<String, Logger>();

private LoggerRuntime runtime = null;

void attacheRuntime(LoggerRuntime runtime) {
this.runtime = runtime;
}

void log(String name, Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
if (runtime != null) {
runtime.log(name, level, marker, messagePattern, arguments, throwable);
}
}

@Override
public org.slf4j.Logger getLogger(String name) {
return loggers.computeIfAbsent(name, n -> new Logger(n, this));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2019-2023 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio.logging.slf4j.bridge;

import org.slf4j.Marker;
import org.slf4j.event.Level;

interface LoggerRuntime {
void log(String name, Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2019-2023 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio.logging.slf4j.bridge;


import org.slf4j.ILoggerFactory;
import org.slf4j.IMarkerFactory;
import org.slf4j.helpers.BasicMDCAdapter;
import org.slf4j.helpers.BasicMarkerFactory;
import org.slf4j.spi.MDCAdapter;

final public class ZioSLF4JServiceProvider implements org.slf4j.spi.SLF4JServiceProvider {
public static final String REQUESTED_API_VERSION = "2.0.99";

private ILoggerFactory loggerFactory;
private IMarkerFactory markerFactory;
private MDCAdapter mdcAdapter;

@Override
public ILoggerFactory getLoggerFactory() {
return loggerFactory;
}

@Override
public IMarkerFactory getMarkerFactory() {
return markerFactory;
}

@Override
public MDCAdapter getMDCAdapter() {
return mdcAdapter;
}

@Override
public String getRequestedApiVersion() {
return REQUESTED_API_VERSION;
}

@Override
public void initialize() {
markerFactory = new BasicMarkerFactory();
loggerFactory = new LoggerFactory();
mdcAdapter = new BasicMDCAdapter();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
zio.logging.slf4j.bridge.ZioSLF4JServiceProvider
Loading