Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
485b98b
Added Logger Kernel Initial File
Jay-Lokhande Jun 18, 2025
8d8223f
sbt scalafmt
Jay-Lokhande Jun 18, 2025
f6e9d55
added withMethodName method
Jay-Lokhande Jun 18, 2025
e7f0be4
added more SAM type specific files
Jay-Lokhande Jun 18, 2025
7f6dc0e
changed LogLevel to remove conflict
Jay-Lokhande Jun 18, 2025
9c3c243
going back to LogLevel name
Jay-Lokhande Jun 18, 2025
9908c00
original WriterTLogger
Jay-Lokhande Jun 18, 2025
4fb2850
deleted DefferedLogLevel
Jay-Lokhande Jun 18, 2025
fc58670
build.sbt
Jay-Lokhande Jun 18, 2025
23b2094
scalafmt
Jay-Lokhande Jun 18, 2025
3f1f34c
sbt scalafmtSbt
Jay-Lokhande Jun 18, 2025
7961c15
added KernelLogLevel
Jay-Lokhande Jun 18, 2025
a5708c3
add new samlogger interface based on old and poc logger
Jay-Lokhande Jun 20, 2025
649468d
add SamStructuredLogger that delegates to LoggerKernel, with original…
Jay-Lokhande Jun 23, 2025
ac5aaee
modify log with parameterized with the context type
Jay-Lokhande Jun 29, 2025
53dda4f
change datatype double to int and use cats Order
Jay-Lokhande Jun 29, 2025
634de7d
update LoggerKernel to use new Log.scala
Jay-Lokhande Jun 29, 2025
b7f0d10
update LogRecord and Recordable to use paramerterized Log
Jay-Lokhande Jun 29, 2025
859753b
update ConsoleLogger to original, JsonLike
Jay-Lokhande Jun 29, 2025
ac1a39b
remove unused Context.Encoder
Jay-Lokhande Jun 29, 2025
3beb5f7
Merge branch 'typelevel:main' into feature/log4cats-sam-type
Jay-Lokhande Aug 7, 2025
44ab02b
remove JsonLike Structure instead bypass it direct
Jay-Lokhande Aug 11, 2025
10b0c81
modify files to adapt new change
Jay-Lokhande Aug 11, 2025
4b746e2
update log.scala
Jay-Lokhande Aug 12, 2025
2ad479c
update recordable
Jay-Lokhande Aug 13, 2025
842bb7e
update Log.scala
Jay-Lokhande Aug 13, 2025
fd6bab1
update log.scala
Jay-Lokhande Aug 26, 2025
63ba4c4
update Log.scala
Jay-Lokhande Aug 26, 2025
5b85513
update with the suggested changes
Jay-Lokhande Sep 4, 2025
022f96b
use builder for the context
Jay-Lokhande Sep 4, 2025
3d00cf9
add sourcecode stable version
Jay-Lokhande Sep 4, 2025
17cb902
sbt scalafmt
Jay-Lokhande Sep 4, 2025
3df4963
sbt scalafmt
Jay-Lokhande Sep 4, 2025
4602d56
add foreach loop instead forALL
Jay-Lokhande Sep 4, 2025
f139d0c
resolve sourcecode scalanavative error
Jay-Lokhande Sep 4, 2025
401c5ad
fix sourcecode library issue
Jay-Lokhande Sep 16, 2025
f88c446
sbt scalafmtSbt
Jay-Lokhande Sep 16, 2025
10cd36c
add test suites
Jay-Lokhande Sep 16, 2025
e8dd135
fix test error
Jay-Lokhande Sep 16, 2025
f9364f1
fix dateTimeFormatter error
Jay-Lokhande Sep 16, 2025
aafe9ba
add Noop Logger with tests
Jay-Lokhande Sep 16, 2025
ad0143e
remove unwanted file
Jay-Lokhande Sep 17, 2025
bd68364
remove LogRecord Recordable and SamLoggerAdapter
Jay-Lokhande Oct 4, 2025
d19ffae
update core's extras
Jay-Lokhande Oct 4, 2025
749fd3d
update old interfaces with logger kernel functionality
Jay-Lokhande Oct 4, 2025
72376e8
remove SamLoggerAdaptorTest
Jay-Lokhande Oct 4, 2025
b0ddff1
update sam implementation after removing LogRecor and Recordable
Jay-Lokhande Oct 4, 2025
58f2b51
update test suite
Jay-Lokhande Oct 4, 2025
b606c3b
update js-console ConsoleLogger
Jay-Lokhande Oct 4, 2025
6962ff8
update slf4j Logger Internel
Jay-Lokhande Oct 4, 2025
041375c
update testing module with LoggerKernel
Jay-Lokhande Oct 4, 2025
468114f
update NoOp Logger
Jay-Lokhande Oct 4, 2025
cb28f49
Merge branch 'typelevel:main' into feature/log4cats-sam-type
Jay-Lokhande Oct 4, 2025
f695de3
Merge branch 'feature/log4cats-sam-type' of https://github.com/Jay-Lo…
Jay-Lokhande Oct 4, 2025
f6bf9a3
fix match level error
Jay-Lokhande Oct 4, 2025
329bc40
fix match error
Jay-Lokhande Oct 4, 2025
a32d908
fix match error
Jay-Lokhande Oct 4, 2025
809e560
fir test errors
Jay-Lokhande Oct 4, 2025
1b7aeb9
try differentt fix
Jay-Lokhande Oct 4, 2025
2a9fe26
fix tests
Jay-Lokhande Oct 4, 2025
3a13966
sbt scala format
Jay-Lokhande Oct 4, 2025
0f8c152
update mima Binary issues
Jay-Lokhande Oct 4, 2025
21b3e6c
add MiMa Exclusions
Jay-Lokhande Oct 4, 2025
e69b27d
remove sourcecode
Jay-Lokhande Oct 4, 2025
8bbf794
remove java.util time class as suggested
Jay-Lokhande Oct 4, 2025
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
29 changes: 28 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,34 @@ lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform)
libraryDependencies ++= {
if (tlIsScala3.value) Seq.empty
else Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided)
}
},
mimaBinaryIssueFilters ++= Seq(
// Removed implicit conversions for monad transformers from Logger - these were intentionally removed
ProblemFilters
.exclude[DirectMissingMethodProblem]("org.typelevel.log4cats.Logger.kleisliLogger"),
ProblemFilters
.exclude[DirectMissingMethodProblem]("org.typelevel.log4cats.Logger.eitherTLogger"),
ProblemFilters
.exclude[DirectMissingMethodProblem]("org.typelevel.log4cats.Logger.optionTLogger"),
// Removed implicit conversions for monad transformers from LoggerFactory - these were intentionally removed
ProblemFilters
.exclude[DirectMissingMethodProblem]("org.typelevel.log4cats.LoggerFactory.kleisliFactory"),
ProblemFilters
.exclude[DirectMissingMethodProblem]("org.typelevel.log4cats.LoggerFactory.eitherTFactory"),
ProblemFilters
.exclude[DirectMissingMethodProblem]("org.typelevel.log4cats.LoggerFactory.optionTFactory"),
// Added new kernel methods - these are new API additions
ProblemFilters.exclude[ReversedMissingMethodProblem]("org.typelevel.log4cats.Logger.kernel"),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"org.typelevel.log4cats.extras.DeferredLogger.kernel"
),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"org.typelevel.log4cats.extras.DeferredSelfAwareStructuredLogger.kernel"
),
ProblemFilters.exclude[ReversedMissingMethodProblem](
"org.typelevel.log4cats.extras.DeferredStructuredLogger.kernel"
)
)
)
.nativeSettings(commonNativeSettings)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2018 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.typelevel.log4cats

import cats.effect.kernel.Sync

/**
* A simple console implementation of LoggerKernel for testing the SAM design.
*/
class ConsoleLoggerKernel[F[_], Ctx](implicit F: Sync[F]) extends LoggerKernel[F, Ctx] {

def log(level: KernelLogLevel, record: Log.Builder[Ctx] => Log.Builder[Ctx]): F[Unit] = {
F.delay {
val logRecord = record(Log.mutableBuilder[Ctx]()).build()

val timestamp = logRecord.timestamp.getOrElse(java.time.Instant.now())
val timeStr = timestamp.toString

val levelStr = logRecord.level.namePadded
val message = logRecord.message
val className = logRecord.className.map(c => s"[$c]").getOrElse("")
val fileName =
logRecord.fileName.map(f => s"($f:${logRecord.line.getOrElse(0)})").getOrElse("")

val contextStr = if (logRecord.context.nonEmpty) {
val contextPairs = logRecord.context.map { case (k, v) => s"$k=$v" }.mkString(", ")
s" {$contextPairs}"
} else ""

val throwableStr = logRecord.throwable.map(t => s"\n${t.toString}").getOrElse("")

val logLine = s"$timeStr $levelStr $className$fileName$contextStr $message$throwableStr"

println(logLine)
}
}
}

object ConsoleLoggerKernel {
def apply[F[_], Ctx](implicit F: Sync[F]): ConsoleLoggerKernel[F, Ctx] =
new ConsoleLoggerKernel[F, Ctx]
}
57 changes: 57 additions & 0 deletions core/shared/src/main/scala/org/typelevel/log4cats/Context.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2018 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.typelevel.log4cats

import scala.concurrent.duration.FiniteDuration

import org.typelevel.log4cats.Context.Encoder

/**
* A value that can be written into a json-like construct, provided a visitor.
*/
trait Context[C] {
def capture[A](a: A)(implicit E: Encoder[A, C]): C
}

object Context {
trait Encoder[A, B] {
def encode(a: A): B
}

object Encoder {
def apply[A, B](implicit ev: Encoder[A, B]): ev.type = ev

// Identity encoder for when input and output types are the same
implicit def identityEncoder[A]: Encoder[A, A] = a => a

implicit val stringToStringEncoder: Encoder[String, String] = a => a

implicit val intToStringEncoder: Encoder[Int, String] = _.toString

implicit val longToStringEncoder: Encoder[Long, String] = _.toString

implicit val doubleToStringEncoder: Encoder[Double, String] = _.toString

implicit val booleanToStringEncoder: Encoder[Boolean, String] = if (_) "true" else "false"

// Removed Instant encoder for Scala Native compatibility
// implicit val instantToStringEncoder: Encoder[Instant, String] =
// DateTimeFormatter.ISO_INSTANT.format(_)

implicit val finiteDurationToStringEncoder: Encoder[FiniteDuration, String] = _.toString
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2018 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.typelevel.log4cats

import cats.Order

final case class KernelLogLevel(name: String, value: Int) {
def namePadded: String = name.padTo(5, ' ').mkString
}

object KernelLogLevel {
implicit final val orderKernelLogLevel: Order[KernelLogLevel] =
Order.by[KernelLogLevel, Int](-_.value)

val Trace: KernelLogLevel = KernelLogLevel("TRACE", 100)
val Debug: KernelLogLevel = KernelLogLevel("DEBUG", 200)
val Info: KernelLogLevel = KernelLogLevel("INFO", 300)
val Warn: KernelLogLevel = KernelLogLevel("WARN", 400)
val Error: KernelLogLevel = KernelLogLevel("ERROR", 500)
val Fatal: KernelLogLevel = KernelLogLevel("FATAL", 600)
}
193 changes: 193 additions & 0 deletions core/shared/src/main/scala/org/typelevel/log4cats/Log.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright 2018 Typelevel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.typelevel.log4cats

import scala.collection.mutable
import scala.concurrent.duration.FiniteDuration

/**
* Low-level interface exposing methods to enrich a log record with relevant information. The
* methods are designed to capture elements that cannot be easily captured from a monadic context
* (or by running an effect). Elements such as timestamps should be provided by means of
* middlewares.
*/
trait Log[Ctx] {
def timestamp: Option[FiniteDuration]
def level: KernelLogLevel
def message: () => String
def throwable: Option[Throwable]
def context: Map[String, Ctx]
def fileName: Option[String]
def className: Option[String]
def methodName: Option[String]
def line: Option[Int]
def levelValue: Int
}

object Log {
trait Builder[Ctx] {
def withTimestamp(value: FiniteDuration): Builder[Ctx]
def withLevel(level: KernelLogLevel): Builder[Ctx]
def withMessage(message: => String): Builder[Ctx]
def withThrowable(throwable: Throwable): Builder[Ctx]
def withContext[A](name: String)(ctx: A)(implicit E: Context.Encoder[A, Ctx]): Builder[Ctx]
def withFileName(name: String): Builder[Ctx]
def withClassName(name: String): Builder[Ctx]
def withMethodName(name: String): Builder[Ctx]
def withLine(line: Int): Builder[Ctx]

final def withContextMap[A](
contextMap: Map[String, A]
)(implicit E: Context.Encoder[A, Ctx]): Builder[Ctx] =
contextMap.foldLeft(this) { case (builder, (k, v)) => builder.withContext(k)(v) }

def adaptTimestamp(f: FiniteDuration => FiniteDuration): Builder[Ctx]
def adaptLevel(f: KernelLogLevel => KernelLogLevel): Builder[Ctx]
def adaptMessage(f: String => String): Builder[Ctx]
def adaptThrowable(f: Throwable => Throwable): Builder[Ctx]
def adaptContext(f: Map[String, Ctx] => Map[String, Ctx]): Builder[Ctx]
def adaptFileName(f: String => String): Builder[Ctx]
def adaptClassName(f: String => String): Builder[Ctx]
def adaptMethodName(f: String => String): Builder[Ctx]
def adaptLine(f: Int => Int): Builder[Ctx]

def build(): Log[Ctx]
}

def mutableBuilder[Ctx](): Builder[Ctx] = new MutableBuilder[Ctx]()

private class MutableBuilder[Ctx] extends Builder[Ctx] {
private var _timestamp: Option[FiniteDuration] = None
private var _level: KernelLogLevel = KernelLogLevel.Info
private var _message: () => String = () => ""
private var _throwable: Option[Throwable] = None
private var _context: mutable.Builder[(String, Ctx), Map[String, Ctx]] =
Map.newBuilder[String, Ctx]
private var _fileName: Option[String] = None
private var _className: Option[String] = None
private var _methodName: Option[String] = None
private var _line: Option[Int] = None

def build(): Log[Ctx] = new Log[Ctx] {
override def timestamp: Option[FiniteDuration] = _timestamp
override def level: KernelLogLevel = _level
override def message: () => String = _message
override def throwable: Option[Throwable] = _throwable
override def context: Map[String, Ctx] = _context.result()
override def className: Option[String] = _className
override def fileName: Option[String] = _fileName
override def methodName: Option[String] = _methodName
override def line: Option[Int] = _line.filter(_ > 0)
override def levelValue: Int = _level.value
}

override def withTimestamp(value: FiniteDuration): this.type = {
_timestamp = Some(value)
this
}

override def withLevel(level: KernelLogLevel): this.type = {
_level = level
this
}

override def withMessage(message: => String): this.type = {
_message = () => message
this
}

override def adaptMessage(f: String => String): this.type = {
val originalMessage = _message
_message = () => f(originalMessage())
this
}

override def adaptTimestamp(f: FiniteDuration => FiniteDuration): this.type = {
_timestamp = _timestamp.map(f)
this
}

override def adaptLevel(f: KernelLogLevel => KernelLogLevel): this.type = {
_level = f(_level)
this
}

override def adaptThrowable(f: Throwable => Throwable): this.type = {
_throwable = _throwable.map(f)
this
}

override def adaptContext(f: Map[String, Ctx] => Map[String, Ctx]): this.type = {
val currentContext = _context.result()
_context = Map.newBuilder[String, Ctx]
f(currentContext).foreach { case (k, v) => _context += (k -> v) }
this
}

override def adaptFileName(f: String => String): this.type = {
_fileName = _fileName.map(f)
this
}

override def adaptClassName(f: String => String): this.type = {
_className = _className.map(f)
this
}

override def adaptMethodName(f: String => String): this.type = {
_methodName = _methodName.map(f)
this
}

override def adaptLine(f: Int => Int): this.type = {
_line = _line.map(f)
this
}

override def withThrowable(throwable: Throwable): this.type = {
_throwable = Some(throwable)
this
}

override def withContext[A](
name: String
)(ctx: A)(implicit E: Context.Encoder[A, Ctx]): this.type = {
_context += (name -> E.encode(ctx))
this
}

override def withFileName(name: String): this.type = {
_fileName = Some(name)
this
}

override def withClassName(name: String): this.type = {
_className = Some(name)
this
}

override def withMethodName(name: String): this.type = {
_methodName = Some(name)
this
}

override def withLine(line: Int): this.type = {
_line = if (line > 0) Some(line) else None
this
}
}
}
Loading