diff options
author | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2015-07-27 15:35:28 +0200 |
---|---|---|
committer | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2015-07-29 15:35:12 +0200 |
commit | 0f0dafbc24fe056dfcffffd2aa9cc68df9b3ee05 (patch) | |
tree | 356284f12de03a17f81cba74a144244cb038cb65 /sonar-core | |
parent | f4f1bf73780a9f174dc382b2c81e4576bef7fbb1 (diff) | |
download | sonarqube-0f0dafbc24fe056dfcffffd2aa9cc68df9b3ee05.tar.gz sonarqube-0f0dafbc24fe056dfcffffd2aa9cc68df9b3ee05.zip |
fork Profiler into sonar-core to change return type of stop methods
Diffstat (limited to 'sonar-core')
6 files changed, 677 insertions, 0 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/util/logs/DefaultProfiler.java b/sonar-core/src/main/java/org/sonar/core/util/logs/DefaultProfiler.java new file mode 100644 index 00000000000..83bbde6e92d --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/util/logs/DefaultProfiler.java @@ -0,0 +1,194 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.core.util.logs; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.LoggerLevel; + +class DefaultProfiler extends Profiler { + + private static final String CONTEXT_SEPARATOR = " | "; + + private final LinkedHashMap<String, Object> context = new LinkedHashMap<>(); + private final Logger logger; + + private long startTime = 0L; + private String startMessage = null; + + public DefaultProfiler(Logger logger) { + this.logger = logger; + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + @Override + public Profiler start() { + this.startTime = System2.INSTANCE.now(); + this.startMessage = null; + return this; + } + + @Override + public Profiler startTrace(String message) { + this.startTime = System2.INSTANCE.now(); + this.startMessage = message; + StringBuilder sb = new StringBuilder(); + sb.append(message); + appendContext(sb); + logger.trace(sb.toString()); + return this; + } + + @Override + public Profiler startDebug(String message) { + this.startTime = System2.INSTANCE.now(); + this.startMessage = message; + StringBuilder sb = new StringBuilder(); + sb.append(message); + appendContext(sb); + logger.debug(sb.toString()); + return this; + } + + @Override + public Profiler startInfo(String message) { + this.startTime = System2.INSTANCE.now(); + this.startMessage = message; + StringBuilder sb = new StringBuilder(); + sb.append(message); + appendContext(sb); + logger.info(sb.toString()); + return this; + } + + + @Override + public long stopTrace() { + return doStopWithoutMessage(LoggerLevel.TRACE); + } + + @Override + public long stopDebug() { + return doStopWithoutMessage(LoggerLevel.DEBUG); + } + + @Override + public long stopInfo() { + return doStopWithoutMessage(LoggerLevel.INFO); + } + + private long doStopWithoutMessage(LoggerLevel level) { + if (startMessage == null) { + throw new IllegalStateException("Profiler#stopXXX() can't be called without any message defined in start methods"); + } + return doStop(level, startMessage, " (done)"); + } + + @Override + public long stopTrace(String message) { + return doStop(LoggerLevel.TRACE, message, ""); + } + + @Override + public long stopDebug(String message) { + return doStop(LoggerLevel.DEBUG, message, ""); + } + + @Override + public long stopInfo(String message) { + return doStop(LoggerLevel.INFO, message, ""); + } + + private long doStop(LoggerLevel level, @Nullable String message, String messageSuffix) { + if (startTime == 0L) { + throw new IllegalStateException("Profiler must be started before being stopped"); + } + long duration = System2.INSTANCE.now() - startTime; + StringBuilder sb = new StringBuilder(); + if (!StringUtils.isEmpty(message)) { + sb.append(message); + sb.append(messageSuffix); + sb.append(CONTEXT_SEPARATOR); + } + sb.append("time=").append(duration).append("ms"); + appendContext(sb); + log(level, sb.toString()); + startTime = 0L; + startMessage = null; + context.clear(); + return duration; + } + + void log(LoggerLevel level, String msg) { + switch (level) { + case TRACE: + logger.trace(msg); + break; + case DEBUG: + logger.debug(msg); + break; + case INFO: + logger.info(msg); + break; + case WARN: + logger.warn(msg); + break; + case ERROR: + logger.error(msg); + break; + default: + throw new IllegalArgumentException("Unsupported LoggerLevel value: " + level); + } + } + + private void appendContext(StringBuilder sb) { + for (Map.Entry<String, Object> entry : context.entrySet()) { + if (sb.length() > 0) { + sb.append(CONTEXT_SEPARATOR); + } + sb.append(entry.getKey()).append("=").append(Objects.toString(entry.getValue())); + } + } + + @Override + public Profiler addContext(String key, @Nullable Object value) { + if (value == null) { + context.remove(key); + } else { + context.put(key, value); + } + return this; + } + +} diff --git a/sonar-core/src/main/java/org/sonar/core/util/logs/NullProfiler.java b/sonar-core/src/main/java/org/sonar/core/util/logs/NullProfiler.java new file mode 100644 index 00000000000..665f71d9c9b --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/util/logs/NullProfiler.java @@ -0,0 +1,96 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.core.util.logs; + +import javax.annotation.Nullable; + +class NullProfiler extends Profiler { + + static final NullProfiler NULL_INSTANCE = new NullProfiler(); + + private NullProfiler() { + } + + @Override + public boolean isDebugEnabled() { + return false; + } + + @Override + public boolean isTraceEnabled() { + return false; + } + + @Override + public Profiler start() { + return this; + } + + @Override + public Profiler startTrace(String message) { + return this; + } + + @Override + public Profiler startDebug(String message) { + return this; + } + + @Override + public Profiler startInfo(String message) { + return this; + } + + @Override + public long stopTrace() { + return 0; + } + + @Override + public long stopDebug() { + return 0; + } + + @Override + public long stopInfo() { + return 0; + } + + @Override + public long stopTrace(String message) { + return 0; + } + + @Override + public long stopDebug(String message) { + return 0; + } + + @Override + public long stopInfo(String message) { + return 0; + } + + @Override + public Profiler addContext(String key, @Nullable Object value) { + // nothing to do + return this; + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/util/logs/Profiler.java b/sonar-core/src/main/java/org/sonar/core/util/logs/Profiler.java new file mode 100644 index 00000000000..eaf79605f5a --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/util/logs/Profiler.java @@ -0,0 +1,81 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.core.util.logs; + +import javax.annotation.Nullable; +import org.sonar.api.utils.log.Logger; + +/** + * + * @since 5.1 + */ +public abstract class Profiler { + + public static Profiler create(Logger logger) { + return new DefaultProfiler(logger); + } + + public static Profiler createIfTrace(Logger logger) { + if (logger.isTraceEnabled()) { + return create(logger); + } + return NullProfiler.NULL_INSTANCE; + } + + public static Profiler createIfDebug(Logger logger) { + if (logger.isDebugEnabled()) { + return create(logger); + } + return NullProfiler.NULL_INSTANCE; + } + + public abstract boolean isDebugEnabled(); + + public abstract boolean isTraceEnabled(); + + public abstract Profiler start(); + + public abstract Profiler startTrace(String message); + + public abstract Profiler startDebug(String message); + + public abstract Profiler startInfo(String message); + + /** + * Works only if a message have been set in startXXX() methods. + */ + public abstract long stopTrace(); + + public abstract long stopDebug(); + + public abstract long stopInfo(); + + public abstract long stopTrace(String message); + + public abstract long stopDebug(String message); + + public abstract long stopInfo(String message); + + /** + * Context information is removed if value is <code>null</code>. + */ + public abstract Profiler addContext(String key, @Nullable Object value); + +} diff --git a/sonar-core/src/test/java/org/sonar/core/util/logs/DefaultProfilerTest.java b/sonar-core/src/test/java/org/sonar/core/util/logs/DefaultProfilerTest.java new file mode 100644 index 00000000000..7a71b45a9b4 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/util/logs/DefaultProfilerTest.java @@ -0,0 +1,192 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.core.util.logs; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.api.utils.log.Loggers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; + +public class DefaultProfilerTest { + + @Rule + public LogTester tester = new LogTester(); + + Profiler underTest = Profiler.create(Loggers.get("DefaultProfilerTest")); + + @Test + public void test_levels() throws Exception { + // trace by default + assertThat(underTest.isDebugEnabled()).isTrue(); + assertThat(underTest.isTraceEnabled()).isTrue(); + + tester.setLevel(LoggerLevel.DEBUG); + assertThat(underTest.isDebugEnabled()).isTrue(); + assertThat(underTest.isTraceEnabled()).isFalse(); + + tester.setLevel(LoggerLevel.INFO); + assertThat(underTest.isDebugEnabled()).isFalse(); + assertThat(underTest.isTraceEnabled()).isFalse(); + } + + @Test + public void stop_reuses_start_message() throws InterruptedException { + tester.setLevel(LoggerLevel.TRACE); + + // trace + underTest.startTrace("Register rules"); + Thread.sleep(2); + assertThat(tester.logs()).containsOnly("Register rules"); + long timing = underTest.stopTrace(); + assertThat(timing).isGreaterThan(0); + assertThat(tester.logs()).hasSize(2); + assertThat(tester.logs().get(1)).startsWith("Register rules (done) | time=" + timing); + tester.clear(); + + // debug + underTest.startDebug("Register rules"); + Thread.sleep(2); + assertThat(tester.logs()).containsOnly("Register rules"); + timing = underTest.stopTrace(); + assertThat(timing).isGreaterThan(0); + assertThat(tester.logs()).hasSize(2); + assertThat(tester.logs().get(1)).startsWith("Register rules (done) | time=" + timing); + tester.clear(); + + // info + underTest.startInfo("Register rules"); + Thread.sleep(2); + assertThat(tester.logs()).containsOnly("Register rules"); + timing = underTest.stopTrace(); + assertThat(timing).isGreaterThan(0); + assertThat(tester.logs()).hasSize(2); + assertThat(tester.logs().get(1)).startsWith("Register rules (done) | time=" + timing); + } + + @Test + public void different_start_and_stop_messages() { + tester.setLevel(LoggerLevel.TRACE); + + // start TRACE and stop DEBUG + underTest.startTrace("Register rules"); + underTest.stopDebug("Rules registered"); + assertThat(tester.logs()).hasSize(2); + assertThat(tester.logs().get(0)).contains("Register rules"); + assertThat(tester.logs().get(1)).startsWith("Rules registered | time="); + tester.clear(); + + // start DEBUG and stop INFO + underTest.startDebug("Register rules"); + underTest.stopInfo("Rules registered"); + assertThat(tester.logs()).hasSize(2); + assertThat(tester.logs().get(0)).contains("Register rules"); + assertThat(tester.logs().get(1)).startsWith("Rules registered | time="); + tester.clear(); + + // start INFO and stop TRACE + underTest.startInfo("Register rules"); + underTest.stopTrace("Rules registered"); + assertThat(tester.logs()).hasSize(2); + assertThat(tester.logs().get(0)).contains("Register rules"); + assertThat(tester.logs().get(1)).startsWith("Rules registered | time="); + } + + @Test + public void log_on_at_stop() { + tester.setLevel(LoggerLevel.TRACE); + + // trace + underTest.start(); + underTest.stopTrace("Rules registered"); + assertThat(tester.logs()).hasSize(1); + assertThat(tester.logs().get(0)).startsWith("Rules registered | time="); + tester.clear(); + + // debug + underTest.start(); + underTest.stopDebug("Rules registered"); + assertThat(tester.logs()).hasSize(1); + assertThat(tester.logs().get(0)).startsWith("Rules registered | time="); + tester.clear(); + + // info + underTest.start(); + underTest.stopInfo("Rules registered"); + assertThat(tester.logs()).hasSize(1); + assertThat(tester.logs().get(0)).startsWith("Rules registered | time="); + } + + @Test + public void add_context() { + org.sonar.core.util.logs.Profiler profiler = Profiler.create(Loggers.get("DefaultProfilerTest")); + profiler.addContext("a_string", "bar"); + profiler.addContext("null_value", null); + profiler.addContext("an_int", 42); + profiler.start(); + // do not write context as there's no message + assertThat(tester.logs()).isEmpty(); + + profiler.addContext("after_start", true); + profiler.stopInfo("Rules registered"); + assertThat(tester.logs()).hasSize(1); + assertThat(tester.logs().get(0)) + .startsWith("Rules registered | time=") + .endsWith("ms | a_string=bar | an_int=42 | after_start=true"); + } + + @Test + public void empty_message() { + underTest.addContext("foo", "bar"); + underTest.startInfo(""); + assertThat(tester.logs()).containsOnly("foo=bar"); + + underTest.addContext("after_start", true); + underTest.stopInfo(""); + assertThat(tester.logs()).hasSize(2); + assertThat(tester.logs().get(1)) + .startsWith("time=") + .endsWith("ms | foo=bar | after_start=true"); + } + + @Test + public void fail_if_stop_without_message() { + underTest.start(); + try { + underTest.stopInfo(); + fail(); + } catch (IllegalStateException e) { + assertThat(e).hasMessage("Profiler#stopXXX() can't be called without any message defined in start methods"); + } + } + + @Test + public void fail_if_stop_without_start() { + try { + underTest.stopDebug("foo"); + fail(); + } catch (IllegalStateException e) { + assertThat(e).hasMessage("Profiler must be started before being stopped"); + } + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/util/logs/NullProfilerTest.java b/sonar-core/src/test/java/org/sonar/core/util/logs/NullProfilerTest.java new file mode 100644 index 00000000000..6572d4956d9 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/util/logs/NullProfilerTest.java @@ -0,0 +1,53 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.core.util.logs; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class NullProfilerTest { + + NullProfiler underTest = NullProfiler.NULL_INSTANCE; + + @Test + public void do_not_fail() { + assertThat(underTest.start()).isSameAs(underTest); + assertThat(underTest.startTrace("")).isSameAs(underTest); + assertThat(underTest.startDebug("")).isSameAs(underTest); + assertThat(underTest.startInfo("")).isSameAs(underTest); + + assertThat(underTest.isDebugEnabled()).isFalse(); + assertThat(underTest.isTraceEnabled()).isFalse(); + assertThat(underTest.addContext("foo", "bar")).isSameAs(underTest); + } + + @Test + public void stop_methods_returns_0() { + underTest.start(); + assertThat(underTest.stopInfo()).isEqualTo(0); + assertThat(underTest.stopInfo("msg")).isEqualTo(0); + assertThat(underTest.stopDebug()).isEqualTo(0); + assertThat(underTest.stopDebug("msg")).isEqualTo(0); + assertThat(underTest.stopTrace()).isEqualTo(0); + assertThat(underTest.stopTrace("msg")).isEqualTo(0); + + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/util/logs/ProfilerTest.java b/sonar-core/src/test/java/org/sonar/core/util/logs/ProfilerTest.java new file mode 100644 index 00000000000..b58a843aa48 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/util/logs/ProfilerTest.java @@ -0,0 +1,61 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.core.util.logs; + +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.api.utils.log.Loggers; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProfilerTest { + @Rule + public LogTester tester = new LogTester(); + + @Test + public void create() { + Profiler profiler = Profiler.create(Loggers.get("foo")); + assertThat(profiler).isInstanceOf(DefaultProfiler.class); + } + + @Test + public void create_null_profiler_if_trace_level_is_disabled() { + tester.setLevel(LoggerLevel.TRACE); + Profiler profiler = Profiler.createIfTrace(Loggers.get("foo")); + assertThat(profiler).isInstanceOf(DefaultProfiler.class); + + tester.setLevel(LoggerLevel.DEBUG); + profiler = Profiler.createIfTrace(Loggers.get("foo")); + assertThat(profiler).isInstanceOf(NullProfiler.class); + } + + @Test + public void create_null_profiler_if_debug_level_is_disabled() { + tester.setLevel(LoggerLevel.TRACE); + Profiler profiler = Profiler.createIfDebug(Loggers.get("foo")); + assertThat(profiler).isInstanceOf(DefaultProfiler.class); + + tester.setLevel(LoggerLevel.INFO); + profiler = Profiler.createIfDebug(Loggers.get("foo")); + assertThat(profiler).isInstanceOf(NullProfiler.class); + } +} |