LOG4J2-1690 Scala wrapper for context map
diff --git a/log4j-api-scala_2.10/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala b/log4j-api-scala_2.10/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala
new file mode 100644
index 0000000..6c91b62
--- /dev/null
+++ b/log4j-api-scala_2.10/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.logging.log4j.scala
+
+import org.apache.logging.log4j.ThreadContext
+
+import scala.collection.{immutable, mutable}
+import scala.collection.JavaConverters._
+
+/** Manages the context data (context map, MDC) that is added to log events.
+ *
+ * A wrapper around `org.apache.logging.log4j.ThreadContext`.
+ */
+object LoggingContext extends mutable.Map[String, String] {
+
+ override def +=(kv: (String, String)): LoggingContext.this.type = {
+ ThreadContext.put(kv._1, kv._2)
+ this
+ }
+
+ override def +=(elem1: (String, String), elem2: (String, String), elems: (String, String)*): LoggingContext.this.type = {
+ val builder = immutable.Map.newBuilder[String,String]
+ builder += elem1
+ builder += elem2
+ builder ++= elems
+ ThreadContext.putAll(builder.result.asJava)
+ this
+ }
+
+ override def ++=(xs: TraversableOnce[(String, String)]): LoggingContext.this.type = {
+ ThreadContext.putAll(xs.toMap.asJava)
+ this
+ }
+
+ override def -=(key: String): LoggingContext.this.type = {
+ ThreadContext.remove(key)
+ this
+ }
+
+ override def -=(elem1: String, elem2: String, elems: String*): LoggingContext.this.type = {
+ val builder = immutable.Seq.newBuilder[String]
+ builder += elem1
+ builder += elem2
+ builder ++= elems
+ ThreadContext.removeAll(builder.result.asJava)
+ this
+ }
+
+ override def --=(xs: TraversableOnce[String]): LoggingContext.this.type = {
+ ThreadContext.removeAll(xs.toSeq.asJava)
+ this
+ }
+
+ override def clear(): Unit = {
+ ThreadContext.clearMap()
+ }
+
+ override def contains(key: String): Boolean = ThreadContext.containsKey(key)
+
+ override def get(key: String): Option[String] = Option(ThreadContext.get(key))
+
+ override def iterator: Iterator[(String, String)] = ThreadContext.getImmutableContext.asScala.iterator
+
+ override def size: Int = ThreadContext.getImmutableContext.size()
+
+ override def isEmpty: Boolean = ThreadContext.isEmpty
+
+}
diff --git a/log4j-api-scala_2.10/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala b/log4j-api-scala_2.10/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala
new file mode 100644
index 0000000..def2164
--- /dev/null
+++ b/log4j-api-scala_2.10/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.logging.log4j.scala
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+import org.scalatest.{FunSuite, Matchers}
+
+@RunWith(classOf[JUnitRunner])
+class LoggingContextTest extends FunSuite with Matchers {
+
+ test("put single, contains, get") {
+ LoggingContext += "key" -> "value"
+
+ LoggingContext.contains("key") shouldBe true
+ LoggingContext.get("key") shouldBe Some("value")
+ LoggingContext.contains("bogus") shouldBe false
+ LoggingContext.get("bogus") shouldBe None
+ }
+
+ test("put multiple 1") {
+ LoggingContext += ("key1" -> "value1", "key2" -> "value2")
+
+ LoggingContext.get("key1") shouldBe Some("value1")
+ LoggingContext.get("key2") shouldBe Some("value2")
+ }
+
+ test("put multiple 2") {
+ LoggingContext ++= Seq("key1" -> "value1", "key2" -> "value2")
+
+ LoggingContext.get("key1") shouldBe Some("value1")
+ LoggingContext.get("key2") shouldBe Some("value2")
+ }
+
+ test("remove single") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext -= "key1"
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe Some("value2")
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("remove multiple 1") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext -= ("key1", "key2")
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe None
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("remove multiple 2") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext --= Seq("key1", "key2")
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe None
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("clear, size, isEmpty") {
+ LoggingContext += ("key" -> "value")
+
+ LoggingContext.clear()
+
+ LoggingContext.contains("key") shouldBe false
+ LoggingContext.size shouldBe 0
+ LoggingContext.isEmpty shouldBe true
+ }
+
+ test("iterator") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+
+ LoggingContext.iterator.toSet shouldBe Set("key1" -> "value1", "key2" -> "value2")
+ }
+
+}
diff --git a/log4j-api-scala_2.11/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala b/log4j-api-scala_2.11/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala
new file mode 100644
index 0000000..70f9ec8
--- /dev/null
+++ b/log4j-api-scala_2.11/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.logging.log4j.scala
+
+import org.apache.logging.log4j.ThreadContext
+
+import scala.collection.JavaConverters._
+import scala.collection.{immutable, mutable}
+
+/** Manages the context data (context map, MDC) that is added to log events.
+ *
+ * A wrapper around `org.apache.logging.log4j.ThreadContext`.
+ */
+object LoggingContext extends mutable.Map[String, String] {
+
+ override def +=(kv: (String, String)): LoggingContext.this.type = {
+ ThreadContext.put(kv._1, kv._2)
+ this
+ }
+
+ override def +=(elem1: (String, String), elem2: (String, String), elems: (String, String)*): LoggingContext.this.type = {
+ val builder = immutable.Map.newBuilder[String,String]
+ builder += elem1
+ builder += elem2
+ builder ++= elems
+ ThreadContext.putAll(builder.result.asJava)
+ this
+ }
+
+ override def ++=(xs: TraversableOnce[(String, String)]): LoggingContext.this.type = {
+ ThreadContext.putAll(xs.toMap.asJava)
+ this
+ }
+
+ override def -=(key: String): LoggingContext.this.type = {
+ ThreadContext.remove(key)
+ this
+ }
+
+ override def -=(elem1: String, elem2: String, elems: String*): LoggingContext.this.type = {
+ val builder = immutable.Seq.newBuilder[String]
+ builder += elem1
+ builder += elem2
+ builder ++= elems
+ ThreadContext.removeAll(builder.result.asJava)
+ this
+ }
+
+ override def --=(xs: TraversableOnce[String]): LoggingContext.this.type = {
+ ThreadContext.removeAll(xs.toSeq.asJava)
+ this
+ }
+
+ override def clear(): Unit = {
+ ThreadContext.clearMap()
+ }
+
+ override def contains(key: String): Boolean = ThreadContext.containsKey(key)
+
+ override def get(key: String): Option[String] = Option(ThreadContext.get(key))
+
+ override def iterator: Iterator[(String, String)] = ThreadContext.getImmutableContext.asScala.iterator
+
+ override def size: Int = ThreadContext.getImmutableContext.size()
+
+ override def isEmpty: Boolean = ThreadContext.isEmpty
+
+}
diff --git a/log4j-api-scala_2.11/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala b/log4j-api-scala_2.11/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala
new file mode 100644
index 0000000..def2164
--- /dev/null
+++ b/log4j-api-scala_2.11/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.logging.log4j.scala
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+import org.scalatest.{FunSuite, Matchers}
+
+@RunWith(classOf[JUnitRunner])
+class LoggingContextTest extends FunSuite with Matchers {
+
+ test("put single, contains, get") {
+ LoggingContext += "key" -> "value"
+
+ LoggingContext.contains("key") shouldBe true
+ LoggingContext.get("key") shouldBe Some("value")
+ LoggingContext.contains("bogus") shouldBe false
+ LoggingContext.get("bogus") shouldBe None
+ }
+
+ test("put multiple 1") {
+ LoggingContext += ("key1" -> "value1", "key2" -> "value2")
+
+ LoggingContext.get("key1") shouldBe Some("value1")
+ LoggingContext.get("key2") shouldBe Some("value2")
+ }
+
+ test("put multiple 2") {
+ LoggingContext ++= Seq("key1" -> "value1", "key2" -> "value2")
+
+ LoggingContext.get("key1") shouldBe Some("value1")
+ LoggingContext.get("key2") shouldBe Some("value2")
+ }
+
+ test("remove single") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext -= "key1"
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe Some("value2")
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("remove multiple 1") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext -= ("key1", "key2")
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe None
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("remove multiple 2") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext --= Seq("key1", "key2")
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe None
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("clear, size, isEmpty") {
+ LoggingContext += ("key" -> "value")
+
+ LoggingContext.clear()
+
+ LoggingContext.contains("key") shouldBe false
+ LoggingContext.size shouldBe 0
+ LoggingContext.isEmpty shouldBe true
+ }
+
+ test("iterator") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+
+ LoggingContext.iterator.toSet shouldBe Set("key1" -> "value1", "key2" -> "value2")
+ }
+
+}
diff --git a/log4j-api-scala_2.12/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala b/log4j-api-scala_2.12/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala
new file mode 100644
index 0000000..70f9ec8
--- /dev/null
+++ b/log4j-api-scala_2.12/src/main/scala/org/apache/logging/log4j/scala/LoggingContext.scala
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.logging.log4j.scala
+
+import org.apache.logging.log4j.ThreadContext
+
+import scala.collection.JavaConverters._
+import scala.collection.{immutable, mutable}
+
+/** Manages the context data (context map, MDC) that is added to log events.
+ *
+ * A wrapper around `org.apache.logging.log4j.ThreadContext`.
+ */
+object LoggingContext extends mutable.Map[String, String] {
+
+ override def +=(kv: (String, String)): LoggingContext.this.type = {
+ ThreadContext.put(kv._1, kv._2)
+ this
+ }
+
+ override def +=(elem1: (String, String), elem2: (String, String), elems: (String, String)*): LoggingContext.this.type = {
+ val builder = immutable.Map.newBuilder[String,String]
+ builder += elem1
+ builder += elem2
+ builder ++= elems
+ ThreadContext.putAll(builder.result.asJava)
+ this
+ }
+
+ override def ++=(xs: TraversableOnce[(String, String)]): LoggingContext.this.type = {
+ ThreadContext.putAll(xs.toMap.asJava)
+ this
+ }
+
+ override def -=(key: String): LoggingContext.this.type = {
+ ThreadContext.remove(key)
+ this
+ }
+
+ override def -=(elem1: String, elem2: String, elems: String*): LoggingContext.this.type = {
+ val builder = immutable.Seq.newBuilder[String]
+ builder += elem1
+ builder += elem2
+ builder ++= elems
+ ThreadContext.removeAll(builder.result.asJava)
+ this
+ }
+
+ override def --=(xs: TraversableOnce[String]): LoggingContext.this.type = {
+ ThreadContext.removeAll(xs.toSeq.asJava)
+ this
+ }
+
+ override def clear(): Unit = {
+ ThreadContext.clearMap()
+ }
+
+ override def contains(key: String): Boolean = ThreadContext.containsKey(key)
+
+ override def get(key: String): Option[String] = Option(ThreadContext.get(key))
+
+ override def iterator: Iterator[(String, String)] = ThreadContext.getImmutableContext.asScala.iterator
+
+ override def size: Int = ThreadContext.getImmutableContext.size()
+
+ override def isEmpty: Boolean = ThreadContext.isEmpty
+
+}
diff --git a/log4j-api-scala_2.12/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala b/log4j-api-scala_2.12/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala
new file mode 100644
index 0000000..def2164
--- /dev/null
+++ b/log4j-api-scala_2.12/src/test/scala/org/apache/logging/log4j/scala/LoggingContextTest.scala
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.logging.log4j.scala
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+import org.scalatest.{FunSuite, Matchers}
+
+@RunWith(classOf[JUnitRunner])
+class LoggingContextTest extends FunSuite with Matchers {
+
+ test("put single, contains, get") {
+ LoggingContext += "key" -> "value"
+
+ LoggingContext.contains("key") shouldBe true
+ LoggingContext.get("key") shouldBe Some("value")
+ LoggingContext.contains("bogus") shouldBe false
+ LoggingContext.get("bogus") shouldBe None
+ }
+
+ test("put multiple 1") {
+ LoggingContext += ("key1" -> "value1", "key2" -> "value2")
+
+ LoggingContext.get("key1") shouldBe Some("value1")
+ LoggingContext.get("key2") shouldBe Some("value2")
+ }
+
+ test("put multiple 2") {
+ LoggingContext ++= Seq("key1" -> "value1", "key2" -> "value2")
+
+ LoggingContext.get("key1") shouldBe Some("value1")
+ LoggingContext.get("key2") shouldBe Some("value2")
+ }
+
+ test("remove single") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext -= "key1"
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe Some("value2")
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("remove multiple 1") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext -= ("key1", "key2")
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe None
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("remove multiple 2") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+ LoggingContext += ("key3" -> "value3")
+
+ LoggingContext --= Seq("key1", "key2")
+
+ LoggingContext.get("key1") shouldBe None
+ LoggingContext.get("key2") shouldBe None
+ LoggingContext.get("key3") shouldBe Some("value3")
+ }
+
+ test("clear, size, isEmpty") {
+ LoggingContext += ("key" -> "value")
+
+ LoggingContext.clear()
+
+ LoggingContext.contains("key") shouldBe false
+ LoggingContext.size shouldBe 0
+ LoggingContext.isEmpty shouldBe true
+ }
+
+ test("iterator") {
+ LoggingContext += ("key1" -> "value1")
+ LoggingContext += ("key2" -> "value2")
+
+ LoggingContext.iterator.toSet shouldBe Set("key1" -> "value1", "key2" -> "value2")
+ }
+
+}