Skip to content

Commit 603b093

Browse files
xerialsjrd
authored andcommitted
Set parent logger reference upon initialization (scala-js#10)
Remove the `findParent` method, which happens to create multiple root loggers with the empty name "". Instead add `prepareParentLoggerOf(name)` to ensure creating and registering the parent loggers in advance. This also reduces the overhead of resolving parent loggers.
1 parent e645744 commit 603b093

File tree

2 files changed

+81
-33
lines changed

2 files changed

+81
-33
lines changed

src/main/scala/java/util/logging/Logger.scala

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import scala.collection.mutable
55

66
object Logger {
77

8-
val GLOBAL_LOGGER_NAME: String = "global"
8+
final val GLOBAL_LOGGER_NAME = "global"
9+
private final val ROOT_LOGGER_NAME = ""
910

1011
// Not implemented, deprecated on JDK 1.8
1112
//val global: Logger
@@ -14,30 +15,57 @@ object Logger {
1415

1516
private val loggers: mutable.Map[String, Logger] = mutable.Map.empty
1617

18+
/** Find parent logger */
19+
@tailrec
20+
private def findParentLoggerOf(name: String): Logger = {
21+
name match {
22+
case null => // Anonymous logger
23+
rootLogger
24+
case ROOT_LOGGER_NAME =>
25+
null
26+
case _ =>
27+
val parentName = name.substring(0, name.lastIndexOf('.').max(0))
28+
loggers.get(parentName) match {
29+
case Some(l) => l
30+
case None => findParentLoggerOf(parentName)
31+
}
32+
}
33+
}
34+
1735
private def newLogger(name: String): Logger = {
1836
val logger = new Logger(name, null)
1937
logger.setLevel(null)
2038
logger.setUseParentHandlers(true)
21-
logger.setParent(rootLogger)
39+
logger.setParent(findParentLoggerOf(name))
40+
if (name != null) {
41+
updateChildLoggerParent(logger)
42+
}
2243
logger
2344
}
2445

46+
private def updateChildLoggerParent(newParent: Logger): Unit ={
47+
val prefix = s"${newParent.getName}."
48+
// Traverse all child loggers of the new parent
49+
for ((name, childLogger) <- loggers if name.startsWith(prefix)) {
50+
val currentParent = childLogger.getParent
51+
// For example, when a new parent a.b is added:
52+
// - if child is a.b.c.d (parent = null) => needs to be a.b.c.d (parent = a.b)
53+
// - if child is a.b.c.e (parent = a.b.c) => no update is required.
54+
if (currentParent == null || !currentParent.getName.startsWith(prefix)) {
55+
childLogger.setParent(newParent)
56+
}
57+
}
58+
}
59+
2560
// Root is not visible to the outside but gives defaults
2661
private[this] val rootLogger: Logger = {
27-
val l = new Logger("", null)
28-
l.setLevel(defaultLogLevel)
29-
l.setUseParentHandlers(false)
30-
l.setParent(null)
31-
l
62+
val root = getLogger(ROOT_LOGGER_NAME)
63+
root.setLevel(defaultLogLevel)
64+
root.setUseParentHandlers(false)
65+
root
3266
}
3367

34-
private[this] val globalLogger: Logger = {
35-
val l = new Logger(GLOBAL_LOGGER_NAME, null)
36-
l.setLevel(defaultLogLevel)
37-
l.setUseParentHandlers(true)
38-
l.setParent(rootLogger)
39-
l
40-
}
68+
private[this] val globalLogger: Logger = getLogger(GLOBAL_LOGGER_NAME)
4169

4270
def getGlobal(): Logger = globalLogger
4371

@@ -51,20 +79,6 @@ object Logger {
5179
// Not implemented, no resource bundle in scala.js
5280
//def getLogger(name: String, resourceBundle: String): Logger
5381

54-
private[logging] def findParent(logger: Logger): Option[Logger] = {
55-
@tailrec
56-
def go(s: List[String]): Option[Logger] = s match {
57-
case Nil => None
58-
59-
case b if loggers.contains(b.mkString(".")) =>
60-
loggers.get(b.mkString("."))
61-
62-
case b => go(b.dropRight(1))
63-
}
64-
65-
go(Option(logger.getName).getOrElse("").split("\\.").toList.dropRight(1))
66-
}
67-
6882
def getAnonymousLogger(): Logger = {
6983
// No references to anonymous loggers are kept
7084
newLogger(null)
@@ -315,7 +329,7 @@ class Logger(name: String, resourceBundle: String) {
315329

316330
def getUseParentHandlers(): Boolean = useParentsHandlers
317331

318-
def getParent(): Logger = Logger.findParent(this).getOrElse(parent)
332+
def getParent(): Logger = parent
319333

320334
def setParent(parent: Logger): Unit = this.parent = parent
321335
}

testSuite/shared/src/test/scala/org/scalajs/testsuite/javalib/util/logging/LoggerTest.scala

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import java.util.logging._
55
import org.junit.{Ignore, Test}
66
import org.junit.Assert._
77

8+
import scala.annotation.tailrec
9+
810
class LoggerTest {
911
// We add a prefix to all loggers. This avoids some errors when running tests
1012
// more than once as the loggers are global
@@ -54,6 +56,10 @@ class LoggerTest {
5456
assertNull(Logger.getAnonymousLogger().getLevel)
5557
assertTrue(Logger.getAnonymousLogger().getUseParentHandlers)
5658
assertNotNull(Logger.getAnonymousLogger().getParent)
59+
60+
// Sanity test of getAnonymousLogger after adding a new logger
61+
val logger = Logger.getLogger(s"$prefix.a")
62+
Logger.getAnonymousLogger()
5763
}
5864

5965
@Test def test_properties(): Unit = {
@@ -337,10 +343,38 @@ class LoggerTest {
337343
}
338344

339345
@Test def test_logger_parents_by_name(): Unit = {
340-
val l1 = Logger.getLogger(s"$prefix.a.b.c.d")
341-
assertEquals("", l1.getParent.getName)
346+
val loggerName = s"${prefix}.a.b.c.d"
347+
var l1 = Logger.getLogger(loggerName)
348+
val p1 = l1.getParent
349+
350+
// l1's parent should be the root logger
351+
val r = Logger.getLogger("")
352+
assertEquals(p1.getName, r.getName)
353+
assertSame(p1, r)
354+
355+
// Add a logger in between root and l1
356+
val l2 = Logger.getLogger(s"${prefix}.a.b")
357+
// l1's parent should be l2
358+
val p2 = l1.getParent
359+
assertEquals(l2.getName, p2.getName)
360+
assertSame(l2, p2)
361+
}
342362

343-
val l2 = Logger.getLogger(s"$prefix.a.b")
344-
assertEquals(l2.getName, l1.getParent.getName)
363+
@Test def test_single_root(): Unit = {
364+
val l1 = Logger.getLogger(s"${prefix}.test1")
365+
val l2 = Logger.getLogger(s"${prefix}.test2.a")
366+
367+
@tailrec
368+
def findRoot(l: Logger): Logger = l.getParent match {
369+
case null => l
370+
case other => findRoot(l.getParent)
371+
}
372+
373+
val r1 = findRoot(l1)
374+
val r2 = findRoot(l2)
375+
val r = Logger.getLogger("")
376+
assertSame(r1, r2)
377+
assertSame(r1, r)
378+
assertSame(r2, r)
345379
}
346380
}

0 commit comments

Comments
 (0)