Index: trunk/test/unit/org/openstreetmap/josm/JOSMFixture.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/JOSMFixture.java	(revision 10892)
+++ trunk/test/unit/org/openstreetmap/josm/JOSMFixture.java	(revision 10899)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.tools.I18n;
+import org.openstreetmap.josm.tools.Logging;
 
 /**
@@ -99,5 +100,5 @@
         Main.platform.preStartupHook();
 
-        Main.logLevel = 3;
+        Logging.setLogLevel(Logging.LEVEL_INFO);
         Main.pref.init(false);
         String url = Main.pref.get("osm-server.url");
Index: trunk/test/unit/org/openstreetmap/josm/MainTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/MainTest.java	(revision 10892)
+++ trunk/test/unit/org/openstreetmap/josm/MainTest.java	(revision 10899)
@@ -3,5 +3,4 @@
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -12,5 +11,4 @@
 import org.junit.Test;
 import org.openstreetmap.josm.Main.DownloadParamType;
-import org.openstreetmap.josm.gui.MainApplication;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -43,14 +41,4 @@
 
     /**
-     * Unit test of {@code Main#preConstructorInit}.
-     */
-    @Test
-    public void testPreConstructorInit() {
-        Main.preConstructorInit(MainApplication.buildCommandLineArgumentMap(new String[0]));
-        Main.preConstructorInit(MainApplication.buildCommandLineArgumentMap(new String[]{"--geometry=400x300+10+5", "--no-maximize"}));
-        //assertEquals(new WindowGeometry(new Point(10, 5), new Dimension(400, 300)), Main.geometry); // FIXME see #12927
-    }
-
-    /**
      * Unit tests on log messages.
      */
@@ -78,21 +66,4 @@
         assertTrue(warnings.contains("W: Warning message on one line"));
         assertTrue(warnings.contains("W: First line of warning message on several lines"));
-
-        int defaultLevel = Main.logLevel;
-
-        // Check levels
-        Main.logLevel = 5;
-        assertTrue(Main.isTraceEnabled());
-        assertTrue(Main.isDebugEnabled());
-
-        Main.logLevel = 4;
-        assertFalse(Main.isTraceEnabled());
-        assertTrue(Main.isDebugEnabled());
-
-        Main.logLevel = 3;
-        assertFalse(Main.isTraceEnabled());
-        assertFalse(Main.isDebugEnabled());
-
-        Main.logLevel = defaultLevel;
     }
 }
Index: trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(revision 10892)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(revision 10899)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.io.OsmTransferCanceledException;
 import org.openstreetmap.josm.tools.I18n;
+import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.MemoryManagerTest;
 import org.openstreetmap.josm.tools.date.DateUtils;
@@ -182,5 +183,5 @@
         TimeZone.setDefault(DateUtils.UTC);
         // Set log level to info
-        Main.logLevel = 3;
+        Logging.setLogLevel(Logging.LEVEL_INFO);
 
         // Set up i18n
Index: trunk/test/unit/org/openstreetmap/josm/tools/LoggingTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/tools/LoggingTest.java	(revision 10899)
+++ trunk/test/unit/org/openstreetmap/josm/tools/LoggingTest.java	(revision 10899)
@@ -0,0 +1,287 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author michael
+ *
+ */
+public class LoggingTest {
+
+    private LogRecord captured;
+    private final Handler handler = new Handler() {
+
+        @Override
+        public void publish(LogRecord record) {
+            captured = record;
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+        }
+    };
+
+    /**
+     * @throws java.lang.Exception
+     */
+    @Before
+    public void setUp() throws Exception {
+        captured = null;
+        Logging.getLogger().addHandler(handler);
+    }
+
+    /**
+     * @throws java.lang.Exception
+     */
+    @After
+    public void tearDown() throws Exception {
+        Logging.getLogger().removeHandler(handler);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#setLogLevel(java.util.logging.Level)}.
+     */
+    @Test
+    public void testSetLogLevel() {
+        Logging.setLogLevel(Logging.LEVEL_DEBUG);
+        assertEquals(Logging.LEVEL_DEBUG, Logging.getLogger().getLevel());
+        Logging.setLogLevel(Logging.LEVEL_WARN);
+        assertEquals(Logging.LEVEL_WARN, Logging.getLogger().getLevel());
+    }
+
+    private void testLogCaptured(Level level, String expected, Runnable printMessage) {
+        testLogCaptured(level, result -> assertEquals(expected, result), printMessage);
+    }
+
+    private void testLogCaptured(Level level, Consumer<String> expectedTester, Runnable printMessage) {
+        Logging.setLogLevel(level);
+        captured = null;
+        printMessage.run();
+
+        expectedTester.accept(captured.getMessage());
+        assertEquals(level, captured.getLevel());
+
+        captured = null;
+        Logging.setLogLevel(Level.OFF);
+        printMessage.run();
+        assertNull(captured);
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#error(java.lang.String)}.
+     */
+    @Test
+    public void testErrorString() {
+        testLogCaptured(Logging.LEVEL_ERROR, "test", () -> Logging.error("test"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#error(java.lang.String, java.lang.Object[])}.
+     */
+    @Test
+    public void testErrorStringObjectArray() {
+        testLogCaptured(Logging.LEVEL_ERROR, "test x 1", () -> Logging.error("test {0} {1}", "x", 1));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#warn(java.lang.String)}.
+     */
+    @Test
+    public void testWarnString() {
+        testLogCaptured(Logging.LEVEL_WARN, "test", () -> Logging.warn("test"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#warn(java.lang.String, java.lang.Object[])}.
+     */
+    @Test
+    public void testWarnStringObjectArray() {
+        testLogCaptured(Logging.LEVEL_WARN, "test x 1", () -> Logging.warn("test {0} {1}", "x", 1));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#info(java.lang.String)}.
+     */
+    @Test
+    public void testInfoString() {
+        testLogCaptured(Logging.LEVEL_INFO, "test", () -> Logging.info("test"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#info(java.lang.String, java.lang.Object[])}.
+     */
+    @Test
+    public void testInfoStringObjectArray() {
+        testLogCaptured(Logging.LEVEL_INFO, "test x 1", () -> Logging.info("test {0} {1}", "x", 1));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#debug(java.lang.String)}.
+     */
+    @Test
+    public void testDebugString() {
+        testLogCaptured(Logging.LEVEL_DEBUG, "test", () -> Logging.debug("test"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#debug(java.lang.String, java.lang.Object[])}.
+     */
+    @Test
+    public void testDebugStringObjectArray() {
+        testLogCaptured(Logging.LEVEL_DEBUG, "test x 1", () -> Logging.debug("test {0} {1}", "x", 1));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#trace(java.lang.String)}.
+     */
+    @Test
+    public void testTraceString() {
+        testLogCaptured(Logging.LEVEL_TRACE, "test", () -> Logging.trace("test"));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#trace(java.lang.String, java.lang.Object[])}.
+     */
+    @Test
+    public void testTraceStringObjectArray() {
+        testLogCaptured(Logging.LEVEL_TRACE, "test x 1", () -> Logging.trace("test {0} {1}", "x", 1));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#log(java.util.logging.Level, java.lang.Throwable)}.
+     */
+    @Test
+    public void testLogLevelThrowable() {
+        testLogCaptured(Logging.LEVEL_ERROR, "java.io.IOException: x", () -> Logging.log(Logging.LEVEL_ERROR, new IOException("x")));
+
+        testLogCaptured(Logging.LEVEL_TRACE, "java.io.IOException: x", () -> Logging.log(Logging.LEVEL_TRACE, new IOException("x")));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#log(java.util.logging.Level, java.lang.String, java.lang.Throwable)}.
+     */
+    @Test
+    public void testLogLevelStringThrowable() {
+        testLogCaptured(Logging.LEVEL_ERROR, "y: java.io.IOException: x", () -> Logging.log(Logging.LEVEL_ERROR, "y", new IOException("x")));
+
+        testLogCaptured(Logging.LEVEL_TRACE, "y: java.io.IOException: x", () -> Logging.log(Logging.LEVEL_TRACE, "y", new IOException("x")));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#logWithStackTrace(java.util.logging.Level, java.lang.Throwable)}.
+     */
+    @Test
+    public void testLogWithStackTraceLevelThrowable() {
+        Consumer<String> test = string -> {
+            assertTrue(string.startsWith("java.io.IOException: x"));
+            assertTrue(string.indexOf("testLogWithStackTraceLevelThrowable") >= 0);
+        };
+        testLogCaptured(Logging.LEVEL_ERROR, test, () -> Logging.logWithStackTrace(Logging.LEVEL_ERROR, new IOException("x")));
+        testLogCaptured(Logging.LEVEL_TRACE, test, () -> Logging.logWithStackTrace(Logging.LEVEL_TRACE, new IOException("x")));
+
+        testLogCaptured(Logging.LEVEL_TRACE, string -> assertTrue(string.startsWith("java.io.IOException\n")),
+                () -> Logging.logWithStackTrace(Logging.LEVEL_TRACE, new IOException()));
+
+        testLogCaptured(Logging.LEVEL_TRACE, string -> assertTrue(string.indexOf("Cause:") >= 0),
+                () -> Logging.logWithStackTrace(Logging.LEVEL_TRACE, new IOException(new IOException())));
+
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#logWithStackTrace(java.util.logging.Level, java.lang.String, java.lang.Throwable)}.
+     */
+    @Test
+    public void testLogWithStackTraceLevelStringThrowable() {
+        Consumer<String> test = string -> {
+            assertTrue(string.startsWith("y: java.io.IOException: x"));
+            assertTrue(string.indexOf("testLogWithStackTraceLevelStringThrowable") > 0);
+        };
+        testLogCaptured(Logging.LEVEL_ERROR, test, () -> Logging.logWithStackTrace(Logging.LEVEL_ERROR, "y", new IOException("x")));
+        testLogCaptured(Logging.LEVEL_TRACE, test, () -> Logging.logWithStackTrace(Logging.LEVEL_TRACE, "y", new IOException("x")));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#isLoggingEnabled(java.util.logging.Level)}.
+     */
+    @Test
+    public void testIsLoggingEnabled() {
+        Logging.setLogLevel(Logging.LEVEL_ERROR);
+        assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_ERROR));
+        assertFalse(Logging.isLoggingEnabled(Logging.LEVEL_INFO));
+        assertFalse(Logging.isLoggingEnabled(Logging.LEVEL_TRACE));
+        Logging.setLogLevel(Logging.LEVEL_INFO);
+        assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_ERROR));
+        assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_INFO));
+        assertFalse(Logging.isLoggingEnabled(Logging.LEVEL_TRACE));
+        Logging.setLogLevel(Logging.LEVEL_TRACE);
+        assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_ERROR));
+        assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_INFO));
+        assertTrue(Logging.isLoggingEnabled(Logging.LEVEL_TRACE));
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#clearLastErrorAndWarnings()}.
+     */
+    @Test
+    public void testClearLastErrorAndWarnings() {
+        Logging.setLogLevel(Logging.LEVEL_WARN);
+        Logging.clearLastErrorAndWarnings();
+        Logging.error("x");
+        assertFalse(Logging.getLastErrorAndWarnings().isEmpty());
+        assertFalse(Logging.getLastErrorAndWarnings().isEmpty());
+        Logging.clearLastErrorAndWarnings();
+        assertTrue(Logging.getLastErrorAndWarnings().isEmpty());
+    }
+
+    /**
+     * Test method for {@link org.openstreetmap.josm.tools.Logging#getLastErrorAndWarnings()}.
+     */
+    @Test
+    public void testGetLastErrorAndWarnings() {
+        Logging.setLogLevel(Logging.LEVEL_WARN);
+        Logging.clearLastErrorAndWarnings();
+        Logging.warn("x");
+
+        assertEquals(1, Logging.getLastErrorAndWarnings().size());
+        assertEquals("W: x", Logging.getLastErrorAndWarnings().get(0));
+
+        Logging.setLogLevel(Logging.LEVEL_ERROR);
+        Logging.warn("x");
+
+        assertEquals(1, Logging.getLastErrorAndWarnings().size());
+
+        Logging.error("y\nz");
+
+        assertEquals(2, Logging.getLastErrorAndWarnings().size());
+        assertArrayEquals(new Object[] {"W: x", "E: y"}, Logging.getLastErrorAndWarnings().toArray());
+
+        // limit somewhere reasonable
+        for (int i = 3; i < 6; i++) {
+            Logging.error("x");
+            assertEquals(i, Logging.getLastErrorAndWarnings().size());
+        }
+        for (int i = 2; i < 100; i++) {
+            Logging.error("x");
+        }
+        assertTrue(Logging.getLastErrorAndWarnings().size() < 101);
+    }
+}
