Merge: Introduce `Logger`, a simple yet powerful logging system
authorJean Privat <jean@pryen.org>
Wed, 3 Jul 2019 18:50:43 +0000 (14:50 -0400)
committerJean Privat <jean@pryen.org>
Wed, 3 Jul 2019 18:50:43 +0000 (14:50 -0400)
# A simple logger for Nit

## Basic Usage

Create a new `Logger` with a severity level threshold set to `warn_level`:

~~~nit
var logger = new Logger(warn_level)
~~~

Messages with a severity equal or higher than `warn_level` will be displayed:

~~~nit
logger.error "Displays an error."
logger.warn "Displays a warning."
~~~

Messages with a lower severity are silenced:

~~~nit
logger.info "Displays nothing."
~~~

`FileLogger` can be used to output the messages into a file:

~~~nit
var log_file = "my.log"

logger = new FileLogger(warn_level, log_file, append = false)
logger.error("An error")
logger.info("Some info")
logger.close

assert log_file.to_path.read_all == "An error\n"
log_file.to_path.delete
~~~

## Severity levels

Each message is associated with a level that indicate its severity.
Only messages with a severity equal to or higher than the logger `level`
threshold will be displayed.

Severity levels from the most severe to the least severe:

* `unknown_level`: An unknown message that should always be outputted.
* `fatal_level`: An unhandleable error that results in a program crash.
* `error_level`: A handleable error condition.
* `warn_level`: A warning.
* `info_level`: Generic (useful) information about system operation.
* `debug_level`: Low-level information for developpers.

## Formatting messages

You can create custom formatters by implementing the `Formatter` interface.

~~~nit
class MyFormatter
super Formatter

redef fun format(level, message) do
if level < warn_level then return message
return "!!!{message}!!!"
end
end
~~~

See `DefaultFormatter` for a more advanced implementation example.

Each Logger can be given a default formatter used to format the every messages
before outputting them:

~~~nit
var formatter = new MyFormatter
var stderr = new StringWriter
var logger = new Logger(warn_level, stderr, formatter)

logger.warn("This is a warning.")
assert stderr.to_s.trim.split("\n").last == "!!!This is a warning.!!!"
~~~

Optionally, a `Formatter` can be given to replace the `default_formatter`
used by default:

~~~nit
# Create a formatter with no default decorator
logger = new Logger(warn_level, stderr, null)

# Display a message without any formatter
logger.warn("This is a warning.")
assert stderr.to_s.trim.split("\n").last == "This is a warning."

# Display a message with a custom formatter
logger.warn("This is a warning.", formatter)
assert stderr.to_s.trim.split("\n").last == "!!!This is a warning.!!!"
~~~

Pull-Request: #2751
Reviewed-by: Jean Privat <jean@pryen.org>

.gitlab-ci.yml
misc/docker/ci/Dockerfile
src/nitunit.nit
tests/sav/nitunit_args1.res
tests/sav/nitunit_args10.res
tests/sav/nitunit_args11.res
tests/sav/nitunit_args12.res
tests/sav/nitunit_args13.res
tests/sav/nitunit_args14.res
tests/sav/nitunit_args9.res
tests/tests.sh

index a0711b3..70b91e1 100644 (file)
@@ -77,7 +77,7 @@ test_some: &test_some
   artifacts:
     paths:
       - tests/errlist
-      - tests/*.xml
+      - tests/*.xml*
     when: always
     reports:
       junit: tests/*.xml
@@ -90,6 +90,7 @@ nitunit_some:
     - git diff --name-only origin/master..HEAD -- "*.nit" "*.res" "README.*" | grep -v "^tests/" > list0.txt || true
     - xargs nitls -pP < list0.txt > list.txt
     - xargs nitunit < list.txt
+    - junit2html nitunit.xml
   artifacts:
     paths:
       - nitunit.xml*
@@ -221,6 +222,7 @@ nitunit_lib:
     - xargs nitunit -v < list.txt| tee log.txt
     - grep -e KO log.txt > status.txt || true
     - tail -3 log.txt >> status.txt
+    - junit2html nitunit.xml
   artifacts:
     paths:
       - nitunit.xml*
@@ -238,6 +240,7 @@ nitunit_src:
     - xargs nitunit -v < list.txt| tee log.txt
     - grep -e KO log.txt > status.txt || true
     - tail -3 log.txt >> status.txt
+    - junit2html nitunit.xml
   artifacts:
     paths:
       - nitunit.xml*
@@ -329,6 +332,21 @@ build_more_tools:
       - src/version.nit
       - src/nitc_0
 
+valgrind:
+  stage: more_test
+  dependencies:
+    - build_more_tools
+  script:
+    - mkdir -p valgrind.out
+    - nitc src/nitc.nit # To warm-up the cache
+    - src/valgrind.sh --callgrind-out-file=valgrind.out/nitc.nitc.out nitc src/nitc.nit -vv
+    - callgrind_annotate valgrind.out/nitc.nitc.out > valgrind.out/nitc.nitc.txt
+    - src/valgrind.sh --callgrind-out-file=valgrind.out/niti.niti.out nit -- src/nit.nit tests/base_simple3.nit -vv
+    - callgrind_annotate valgrind.out/niti.niti.out > valgrind.out/niti.niti.txt
+  artifacts:
+    paths:
+      - valgrind.out
+
 build_doc:
   stage: more_test
   dependencies:
@@ -340,6 +358,16 @@ build_doc:
     paths:
       - nitdoc.out
 
+nitmetrics:
+  stage: more_test
+  dependencies:
+    - build_more_tools
+  script:
+    - nitmetrics --all --log --log-dir nitmetrics.out --dir nitmetrics.out --keep-going lib src
+  artifacts:
+    paths:
+      - nitmetrics.out
+
 build_catalog:
   stage: more_test
   dependencies:
index e4ba62b..69dbc45 100644 (file)
@@ -13,6 +13,7 @@ RUN dpkg --add-architecture i386 \
                graphviz \
                libunwind-dev \
                pkg-config \
+               libicu-dev \
                # Get the code!
                git \
                ca-certificates \
index fe8fb7b..125c514 100644 (file)
@@ -84,19 +84,23 @@ for a in args do
        end
        # Try to load the file as a markdown document
        var mdoc = modelbuilder.load_markdown(a)
-       page.add modelbuilder.test_mdoc(mdoc)
+       var ts = modelbuilder.test_mdoc(mdoc)
+       if not ts.children.is_empty then page.add ts
 end
 
 for a in module_files do
        var g = modelbuilder.identify_group(a)
        if g == null then continue
-       page.add modelbuilder.test_group(g)
+       var ts = modelbuilder.test_group(g)
+       if not ts.children.is_empty then page.add ts
 end
 
 for m in mmodules do
-       page.add modelbuilder.test_markdown(m)
-       var ts = modelbuilder.test_unit(m)
-       if ts != null then page.add ts
+       var ts
+       ts = modelbuilder.test_markdown(m)
+       if not ts.children.is_empty then page.add ts
+       ts = modelbuilder.test_unit(m)
+       if ts != null and not ts.children.is_empty then page.add ts
 end
 
 var file = toolcontext.opt_output.value
index f1851ee..7cec735 100644 (file)
@@ -35,5 +35,5 @@ Test suites: Classes: 1; Test Cases: 3; Failures: 1
 </system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="foo1" time="0.0"><failure message="Syntax Error: unexpected operator &#39;!&#39;."></failure><system-out>assert !@#$%^&amp;*()
 </system-out></testcase><testcase classname="nitunit.test_nitunit.X" name="foo2" time="0.0"><system-err></system-err><system-out>var x = new X
 assert x.foo2
-</system-out></testcase></testsuite><testsuite package="test_test_nitunit::test_test_nitunit"></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo1" time="0.0"><error message="Runtime Error in file nitunit.out&#47;gen_test_test_nitunit.nit">Runtime error: Assert failed (test_test_nitunit.nit:38)
+</system-out></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo1" time="0.0"><error message="Runtime Error in file nitunit.out&#47;gen_test_test_nitunit.nit">Runtime error: Assert failed (test_test_nitunit.nit:38)
 </error></testcase><testcase classname="nitunit.test_test_nitunit.TestX" name="test_foo2" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
index 5fdb314..cbfc0cd 100644 (file)
@@ -5,4 +5,4 @@
 Docunits: Entities: 4; Documented ones: 0; With nitunits: 0
 Test suites: Classes: 1; Test Cases: 2; Failures: 0
 [SUCCESS] All 2 tests passed.
-<testsuites><testsuite package="test_nitunit5::test_nitunit5"></testsuite><testsuite package="test_nitunit5"><testcase classname="nitunit.test_nitunit5.TestNitunit5" name="test_path_is_set" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_nitunit5.TestNitunit5" name="test_path_is_suite_path" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit5"><testcase classname="nitunit.test_nitunit5.TestNitunit5" name="test_path_is_set" time="0.0"><system-err></system-err></testcase><testcase classname="nitunit.test_nitunit5.TestNitunit5" name="test_path_is_suite_path" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
index 0d1829d..a640323 100644 (file)
@@ -13,4 +13,4 @@ Docunits: Entities: 5; Documented ones: 0; With nitunits: 0
 Test suites: Classes: 1; Test Cases: 3; Failures: 3
 [FAILURE] 3/3 tests failed.
 `nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit6::test_nitunit6"></testsuite><testsuite package="test_nitunit6"><testcase classname="nitunit.test_nitunit6.TestNitunit6" name="test_foo" time="0.0"><failure message="Nitunit Error: before module test failed"></failure></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit6"><testcase classname="nitunit.test_nitunit6.TestNitunit6" name="test_foo" time="0.0"><failure message="Nitunit Error: before module test failed"></failure></testcase></testsuite></testsuites>
\ No newline at end of file
index 9015b4a..4932785 100644 (file)
@@ -11,4 +11,4 @@ Docunits: Entities: 5; Documented ones: 0; With nitunits: 0
 Test suites: Classes: 1; Test Cases: 3; Failures: 1
 [FAILURE] 1/3 tests failed.
 `nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit7::test_nitunit7"></testsuite><testsuite package="test_nitunit7"><testcase classname="nitunit.test_nitunit7.TestNitunit7" name="test_foo" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit7"><testcase classname="nitunit.test_nitunit7.TestNitunit7" name="test_foo" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
index 8c8d3f9..84dbc07 100644 (file)
@@ -11,4 +11,4 @@ Docunits: Entities: 3; Documented ones: 0; With nitunits: 0
 Test suites: Classes: 1; Test Cases: 3; Failures: 1
 [FAILURE] 1/3 tests failed.
 `nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit8::test_nitunit8"></testsuite><testsuite package="test_nitunit8"><testcase classname="nitunit.test_nitunit8.TestNitunit8" name="test_foo" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit8"><testcase classname="nitunit.test_nitunit8.TestNitunit8" name="test_foo" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
index c0f1e23..3cb2d30 100644 (file)
@@ -15,4 +15,4 @@ Docunits: Entities: 7; Documented ones: 0; With nitunits: 0
 Test suites: Classes: 1; Test Cases: 7; Failures: 1
 [FAILURE] 1/7 tests failed.
 `nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit11::test_nitunit11"></testsuite><testsuite package="test_nitunit11"><testcase classname="nitunit.test_nitunit11.TestNitunit11" name="test_baz" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+<testsuites><testsuite package="test_nitunit11"><testcase classname="nitunit.test_nitunit11.TestNitunit11" name="test_baz" time="0.0"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
index 404c270..7682e35 100644 (file)
@@ -59,11 +59,11 @@ Docunits: Entities: 22; Documented ones: 0; With nitunits: 0
 Test suites: Classes: 3; Test Cases: 8; Failures: 7
 [FAILURE] 7/8 tests failed.
 `nitunit.out` is not removed for investigation.
-<testsuites><testsuite package="test_nitunit4&gt;"></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4::test_bad_comp"></testsuite><testsuite package="test_bad_comp"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">test_nitunit4&#47;test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
+<testsuites><testsuite package="test_bad_comp"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">test_nitunit4&#47;test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
 </failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">test_nitunit4&#47;test_bad_comp.nit:25,10--19: Error: method or variable `bad_method` unknown in `TestSuiteBadComp`.
-</failure></testcase></testsuite><testsuite package="test_nitunit4::test_bad_comp2"></testsuite><testsuite package="test_bad_comp2"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">nitunit.out&#47;gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
+</failure></testcase></testsuite><testsuite package="test_bad_comp2"><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_good" time="0.0"><failure message="Compilation Error">nitunit.out&#47;gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
 </failure></testcase><testcase classname="nitunit.test_nitunit4.TestSuiteBadComp" name="test_bad" time="0.0"><failure message="Compilation Error">nitunit.out&#47;gen_test_bad_comp2.nit:11,10--17: Error: expected 1 argument(s) for `test_bad(param: Bool)`; got 0. See introduction at `test_nitunit4::TestSuiteBadComp::test_bad`.
-</failure></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4"></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_foo" time="0.0"><error message="Runtime Error in file nitunit.out&#47;gen_test_nitunit4.nit">Before Test
+</failure></testcase></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_foo" time="0.0"><error message="Runtime Error in file nitunit.out&#47;gen_test_nitunit4.nit">Before Test
 Tested method
 After Test
 Runtime assert: &lt;TestTestSuite&gt;.before
@@ -82,4 +82,4 @@ After Test
 </error></testcase><testcase classname="nitunit.test_nitunit4.TestTestSuite" name="test_sav_conflict" time="0.0"><error message="Conflicting expected output: test_nitunit4&#47;test_nitunit4.sav&#47;test_sav_conflict.res, test_nitunit4&#47;sav&#47;test_sav_conflict.res and test_nitunit4&#47;test_sav_conflict.res all exist">Before Test
 Tested method
 After Test
-</error></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite></testsuites>
\ No newline at end of file
+</error></testcase></testsuite></testsuites>
\ No newline at end of file
index 721171d..c19a9a8 100755 (executable)
@@ -846,6 +846,10 @@ fi
 
 echo >>$xml "</testsuite></testsuites>"
 
+if type junit2html >/dev/null; then
+       junit2html "$xml"
+fi
+
 if [ -n "$nok" ]; then
        exit 1
 else