B4J Library jColorLogger - colorful console and console input

jColorLogger allows you to control the text color and background color and column alignment of statements logged to Standard Out in the Windows console and Linux or Mac terminal emulators. The color functionality doesn't work in the B4J IDE but the logging still does, it just appears as boring monochromatic text.

You can also use this library to get text input from the console both synchronously and asynchronously. This gives you a new way to control your non-UI apps (like servers).

This library makes use of the JNA project. You need the jna-4.1.0.jar which you can download here: https://github.com/twall/jna (at the link "jna.jar" in the Downloads section). Place the jna-4.1.0.jar in your B4J external libraries folder. The native DLLs are inspired by code featured in this article: http://www.cplusplus.com/articles/Eyhv0pDG/ . They are packaged into the library so you don't need to download anything extra, just the .zip attached to this post. Extract the .jar and .xml files contained therein to your B4J external libraries folder.

The library was compiled against JDK 6u26 so it should work on JDKs 6,7 and 8. Furthermore, I've written 64-bit and 32-bit DLLs for the library which means this library works on Windows in 32- and 64-bit JVMs. The Mac and Linux side don't use native access so this library can be used on 32- and 64-bit JVMs in those operating systems.

The following example code:
B4X:
Sub Process_Globals
    Dim cl As ColorLogger
End Sub

Sub AppStart (Args() As String)
    cl.Initialize("green", "black",false).ColorLogLine("ColorLogger initialized to these colors.", "", "")

    Log("This is a normal B4J Log() call.")

    cl.ColorLogLine("This sentence is red.", "aqua", "")
    cl.ColorLogLine("The following sentence is green.", "", "")
    cl.ColorLogLine("The previous sentence was blue.", "red", "aqua")

    Log("Another normal B4J Log() call.")

    cl.ColorLogLine(" ", "", "")
    cl.ColorLogWord("OK, ", "", "").ColorLogWord("now seriously, ", "red", "").ColorLogWord("this is ", "aqua", "").ColorLogLine("yellow.", "yellow", "")
    cl.ColorLogLine("Don't use red on purple.", "red", "purple")
    cl.ColorLogWord("Default Back: ", "green", "black").ColorLogLine(cl.DefaultBackColor, cl.DefaultBackColor, "white")
    cl.ColorLogWord("Default Text: ", "green", "black").ColorLogLine(cl.DefaultTextColor, cl.DefaultTextColor, "white")
    cl.ColorLogLine(" ", "", "")

    cl.DefaultBackColor = "dark_white"
    cl.DefaultTextColor = "black"

    cl.ColorLogWord("New Default Back: ", "green", "black").ColorLogLine(cl.DefaultBackColor, cl.DefaultBackColor, "white")
    cl.ColorLogWord("New Default Text: ", "green", "black").ColorLogLine(cl.DefaultTextColor, cl.DefaultTextColor, "white")
    cl.ColorLogLine("", "", "")

End Sub

produces the following output in the Windows console:
Capture.JPG


EDIT(10JUL2015): Updated to version 1.1. See post #3 for details and a code example.
EDIT(11JUL2015): Fixed a minor bug. When multiple threads would try to log simultaneously, the colors weren't registered accurately. Now they are. The version number remains the same.
EDIT(12JUL2015): Updated to version 1.2. See post #4 for details and a code example.
EDIT(3AUG2015): Updated to version 1.3. See post #5 for details and a code example.
EDIT(4AUG2015): Updated to version 1.31. See post #6 for details and a code example.
EDIT(14AUG2015): Updated to version 1.4. See post #7 for details and a super cool code example.
EDIT(24OCT2015): Updated to version 1.5. Minor bugfix.
EDIT(12MAY2018): If you get an UnsatisfiedLinkError, you might need to install the Microsoft Visual C++ Redistributable Package 2010 (vcredist2010).
 

Attachments

  • jColorLogger1.2.zip
    41.6 KB · Views: 224
  • jColorLogger1.3.zip
    43 KB · Views: 229
  • jColorLogger1.31.zip
    43.2 KB · Views: 193
  • jColorLogger1.4.zip
    20.5 KB · Views: 218
  • jColorLogger1.5.zip
    20.5 KB · Views: 308
Last edited:

Roycefer

Well-Known Member
Licensed User
jColorLogger has been updated to version 1.1. The library should now work with any terminal emulator that properly handles ANSI escape codes. Almost all Linux and MacOS terminal emulators should fit this criteria. And this added functionality doesn't use any native access so it should work on 32-bit and 64-bit JVMs on those operating systems. I've only tested it on MSYS2 (a Linux terminal emulator that runs in Windows) and the terminal emulator in NetBeans so feel free to report on whether or not it works on your systems.

When using this library anywhere that properly handles ANSI escape codes, be sure to set the ColorLogger to use ansiMode in the Initialize function. The Windows console and the B4J IDE do not, so set ansiMode to False in those cases.

The following sample code:
B4X:
Sub Process_Globals
    Dim cl As ColorLogger
    Dim lore As AWTRobot
    Dim colArray() As String = Array As String("black", "dark_blue", "dark_green", "dark_aqua", "dark_red", _
        "dark_purple", "dark_yellow", "dark_white", "gray", "blue", "green", "aqua", "red", "purple", _
        "yellow", "white")
    Dim ansiMode As Boolean = False
End Sub

Sub AppStart (Args() As String)
    If Args.Length>0 Then
        Dim argsList As List
        argsList.Initialize
        argsList.AddAll(Args)
        'Captures the "ansiMode" command line arg and sets ansiMode to True
        If argsList.IndexOf("ansiMode")>-1 Then ansiMode = True
    End If
    cl.Initialize("green", "black", ansiMode).ColorLogLine("ColorLogger initialized to these colors.", "", "")
  
    cl.ColorLogWord("    ", "", "")
    For j = 0 To colArray.Length-1
        If j<10 Then
            cl.ColorLogWord(j & "   ", "", "")
        Else
            cl.ColorLogWord(j & "  ", "", "")
        End If
    Next
    cl.ColorLogLine(" ", "", "")
    For j = 0 To colArray.Length-1
        If j<10 Then
            cl.ColorLogWord(j & " :","","")
        Else
            cl.ColorLogWord(j & ":","","")
        End If
        For i = 0 To colArray.Length-1
            cl.ColorLogWord(" 0 ", colArray(j), colArray(i)).ColorLogWord(" ", "","")
        Next
        cl.ColorLogLine(" ", "", "")
    Next

End Sub
produces this output in the normal Windows console (cmd.exe):
CMDColors.PNG

and this output in MSYS2, a Linux terminal emulator running in Windows:
MSYS2Colors.PNG

Note how in the MSYS2 example, I passed the "ansiMode" command line arg to the code to force the ColorLogger to Initialize in ansiMode. In the Windows cmd.exe example, I didn't pass any command line args and the ColorLogger didn't use ansiMode.

In the B4J IDE, you should also not use ansiMode. If you use ansiMode where it's not needed (the B4J IDE, the cmd.exe Windows console), you will get a lot of seemingly meaningless text mangling your logging output.
 
Last edited:

Roycefer

Well-Known Member
Licensed User
Version 1.2 adds the ColorLogWordRight/Left/Center functions which allow you to align your Strings within a column to the right, left or center, respectively.
The code that produces the output in post #3 is now much cleaner looking:
B4X:
    cl.ColorLogWordCenter("", "", "", 5)
    For j = 0 To colArray.Length-1
        cl.ColorLogWordCenter(j, "", "", 4)
    Next
    cl.ColorLogLine(" ", "", "")
    For j = 0 To colArray.Length-1
        cl.ColorLogWordCenter(j, "", "", 4).ColorLogWord(":", "", "")
        For i = 0 To colArray.Length-1
            cl.ColorLogWordCenter("0", colArray(j), colArray(i),3).ColorLogWord(" ", "","")
        Next
        cl.ColorLogLine(" ", "", "")
    Next

If you find yourself logging a lot of data in a table format, you'll soon find that the B4J IDE isn't well suited to displaying it. Not only does it not provide colors, but it uses a non-fixed-width font that makes creating columns of consistent width an unmanageable task. Most consoles and terminal emulators use a fixed-width font, making columns of data easy to display. Consider the following code:
B4X:
    For j = 0 To 19
        For i = 0 To 9
            Dim  k As Int = j*10+i   'k will range from 0 to 199
            Dim tcol As String
            Dim bcol As String
            If k mod 7==0 Then    'multiples of 7 are yellow
                tcol = "yellow"
            Else if k mod 4==0 Then    'multiples of 4 are purple
                tcol = "purple"
            Else                       'otherwise aqua
                tcol = "aqua"
            End If
            If isPrime(k) Then    'primes are highlighted in red
                bcol = "red"
            Else               'otherwise, default back color is used
                bcol = ""
            End If
            cl.ColorLogWordCenter(k, tcol, bcol, 4).ColorLogWord(" ", "", "")
        Next
        cl.ColorLogLine(" ", "", "")
    Next
In the B4J IDE, it looks like this:
IDECols.PNG

while the same code produces this output in the Windows console:
ColumnTest.PNG
 
Last edited:

Roycefer

Well-Known Member
Licensed User
Version 1.3 sees the inclusion of two new methods: GetConsoleInput and GetConsoleInputAsync. The former will block while the latter will return immediately. When the user inputs something into the console, the _NewConsoleInput event will be raised in the Main thread. You may continue to write text to Standard Out while the ColorLogger is waiting for an asynchronous input.

In some situations, console input won't be supported. The IDE is the most obvious case. In these situations, GetConsoleInput will immediately return "Input Not Supported" whereas GetConsoleInputAsync will print "Input Not Supported" to Standard Out.

In the code example below, we get two examples of synchronous user input and then wait for one example of asynchronous user input while the program continues to print text to Standard Out.
B4X:
Sub Process_Globals
    Dim cl As ColorLogger
    Dim langMap As Map = CreateMap("C++":"Bjarne Stroustrup", "Java":"James Gosling", "B4X":"Erel Uziel")
    Dim t As Timer
End Sub

Sub AppStart (Args() As String)
    cl.Initialize("green", "black", false, "cl").ColorLogLine("ColorLogger initialized to these colors.", "", "")
    t.Initialize("t", 2000)
    cl.ColorLogWord("Enter a language: ", "aqua", "")
    Dim s As String = cl.GetConsoleInput
    cl.ColorLogLine(" ", "", "")
    cl.ColorLogWord("The creator of ", "", "").ColorLogWord(s, "yellow", "").ColorLogWord(" is ", "", "").ColorLogWord(langMap.Get(s), "yellow", "").ColorLogLine(" ", "", "")
    cl.ColorLogWord("Enter a language: ", "aqua", "")
    s = cl.GetConsoleInput
    cl.ColorLogLine(" ", "", "")
    cl.ColorLogWord("The creator of ", "", "").ColorLogWord(s, "yellow", "").ColorLogWord(" is ", "", "").ColorLogWord(langMap.Get(s), "yellow", "").ColorLogLine(" ", "", "")
    cl.ColorLogWord("Enter a language asynchronously. ", "aqua", "").GetConsoleInputAsync.ColorLogLine(" ", "", "")

    'All the logging output below and in the t_Tick event sub is occurring while
    'simultaneously waiting for async input

    Log(" ")
    Log("We are waiting for an asynchronous entry")
    Log("Still waiting...")
    Log("Take your time, no rush...")
    Log(" ")
    t.Enabled = True
    StartMessageLoop
End Sub

Sub t_Tick
    Log("tick")
    Log("tock")
End Sub

Sub cl_NewConsoleInput(Text As String)
    t.Enabled = False
    cl.ColorLogWord("Asynchronously, the creator of ", "", "").ColorLogWord(Text, "yellow", "").ColorLogWord(" is ", "", "")
    cl.ColorLogWord(langMap.Get(Text), "yellow", "").ColorLogLine(" ", "", "")
    Log("Stopping Message Loop, ending program")
    StopMessageLoop
End Sub
The output produced is this:
Input.PNG

Note that the ColorLogger stops watching for console input when the _NewConsoleInput event is raised. If you want to continue watching for console input, just call cl.GetConsoleInputAsync inside the event.

This new capability gives you a simple, powerful and straightforward way to control your non-UI apps like servers.
 
Last edited:

Roycefer

Well-Known Member
Licensed User
It occurs to me that B4X programmers who are unfamiliar with dealing directly with console input might find the
B4X:
GetConsoleInput
method counter-intuitive so I've made some changes in version 1.31.
B4X:
GetConsoleInput
is now
B4X:
GetConsoleInputWord
. It still returns the next space-separated String in the console's input buffer. But note that if the user had previously entered a list of space-separated Strings and the buffer hadn't been cleared, these methods would return immediately with the next String in the buffer, without waiting for user input. To make that more intuitive, I've added
B4X:
GetConsoleInputLine
which will return the next newline-separated String. If there are multiple space-separated Strings in the buffer, it will return all of them as one String, up until the next newline character. You should typically use this method over the Word version.

Also, the asynchronous method has been changed to look for newline-separated Strings instead of space-separated Strings. Its name is changed to
B4X:
GetConsoleInputLineAsync
.

Also, you should only have one
B4X:
GetConsoleInputLineAsync
running at a time. If you call a second one before the first one has raised its event, the second one will give you an "Input Not Supported". However you can call that method in the NewConsoleInput event sub.
 

Roycefer

Well-Known Member
Licensed User
The update to 1.4 sees the DLLs compiled with msvc instead of mingw as they were before 1.4. I'm sure we all hate to succumb to The Man but it's probably better this way. The DLLs are leaner, now, meaning the library is a bit smaller. And compiling with msvc ensures that the DLLs will work on all Windows systems, no extra dependencies required. Also, before 1.4, the library wouldn't work on 32-bit JVMs in Windows. Now, it does.

Here's a little console program that displays an animated, colored progress bar (yes, in the console). It's just running on a timer but it simulates a lengthy download. All the magic happens in the logProgressBar() sub.
B4X:
Sub Process_Globals
    Dim cl As ColorLogger
    Dim t As Timer
    Dim counter As Int = 0
    Dim cols() As String = Array As String("dark_yellow", "dark_red", "dark_purple", "purple", "yellow", "red" )
End Sub

Sub AppStart (Args() As String)
    cl.Initialize("green", "black", False, "cl").ColorLogLine("Starting program", "", "")
    t.Initialize("t", 500)
    t.Enabled = True
    StartMessageLoop
End Sub

Sub t_Tick
    counter = counter + 1
    logProgressBar(68, counter/30)
    If counter>29 Then
        t.Enabled = False
        StopMessageLoop
    End If
End Sub

Sub logProgressBar(length As Int, progress As Double)
    Dim numOfChars As Int = length*progress
    Dim progString As String = NumberFormat2(100*progress, 0, 1, 1, False) & "%"
    cl.ColorLogWord(Chr(13), "", "")
    cl.ColorLogWord("[", "aqua", "")
    Dim increment As Int = (length-1)/cols.Length
    For j = 0 To length-1
        If j==Floor((length-1)/2-3) Then
            cl.ColorLogWordRight(progString, "aqua", "",6)
            j = j + 6 
        Else
            If j<numOfChars Then
                cl.ColorLogWord("=", cols(Min(j/increment, cols.Length-1)), "")
            Else
                cl.ColorLogWord(" ", "", "")
            End If
        End If
    Next
    cl.ColorLogWord("] ", "aqua", "")
    cl.ColorLogWord(NumberFormat2(Rnd(0,50)/10, 1,2,2, False), "aqua", "dark_blue").ColorLogWord(" MB/s", "", "")
End Sub
It produces output that looks like this:
pb.gif
 
Last edited:

ksdroid

Member
Licensed User
can't get working on Ubuntu 12.04 lts its probably a bug in jna but anyway this is what i get

main._process_globals (java line: 94)
java.lang.UnsatisfiedLinkError: Unable to load library 'libColorLib': Native lib rary (linux-x86-64/liblibColorLib.so) not found in resource path ([file:/root/Si ege/jColorLogger.jar])
at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:277)
at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:403)
at com.sun.jna.Library$Handler.<init>(Library.java:147)
at com.sun.jna.Native.loadLibrary(Native.java:502)
at com.sun.jna.Native.loadLibrary(Native.java:481)
at butt.droid.colorlogger.ColorLogger$CLog.<clinit>(ColorLogger.java:38)
at butt.droid.colorlogger.ColorLogger.<init>(ColorLogger.java:52)
at b4j.example.main._process_globals(main.java:94)
at b4j.example.main.initializeProcessGlobals(main.java:85)
at b4j.example.main.main(main.java:28)

someone a solution?
 

Roycefer

Well-Known Member
Licensed User
Make sure you set ansiMode to True when running on non-Windows systems. You can do this with the ansiMode property or in the Initialize() method. Make sure to read the comments for the Initialize() method. This is all explained in there.
 

ksdroid

Member
Licensed User
same result as before

java.lang.UnsatisfiedLinkError: Unable to load library 'libColorLibMSVC': Native library (linux-x86-64/liblibColorLibMSVC.so) not found in resource path ([file:/root/Siege/HelloWorld.jar])

i tried to run it with a newer version of jna but that didn't work either
on windows it runs just fine (with Ansi false)
 

Roycefer

Well-Known Member
Licensed User
Are you setting ansiMode to True in the Initialize() method? Are you using the latest version of the jColorLogger library?
 

ksdroid

Member
Licensed User
Yes its initialized I used the same method as in your example and I tried it with ansi mode on in init and without both won't work
What i've tried so far
-New Version of jna
-jna 4.10
-older version of jna
-older version of lib
-newest version of lib
-with ansi ON at init
-with ansi OFF at init

Got no clue why it is not working but it looks like it's not the library
All errors have something to do with jna i'll try to figure it out and will let u know if I know more

Edit: it's crashing before it even gets to init
 

Roycefer

Well-Known Member
Licensed User
The newest version of the jColorLogger library does not make any use of JNA if you've set ansiMode to True in the ColorLogger.Initialize() method. Native access is only used on Windows. On Linux, the library uses the ANSI escape codes to add color to the terminal. The stack trace you posted earlier shows an Exception thrown by JNA inside the default constructor for the ColorLogger class. This is only possible in older versions of the jColorLogger library. This cannot happen in the newest version.

Do the following:
  1. Make sure you are using jColorLogger1.5 (verify this in the Libraries tab of the B4J IDE; right-click>refresh if you need to).
  2. Make sure you are using jna-4.1.0.jar (though this shouldn't matter on Linux, let's just play it safe).
  3. Make sure you are setting ansiMode=True in the ColorLogger.Initialize() method.
  4. Compile and run your app on Linux from the terminal.
  5. If the app crashes, post your stack trace here so I can look at it.
  6. Probably post your code, as well.
I use this library quite extensively on Windows and on the RasPi2 (running Raspbian). Does this library work for you on Windows?
 
Last edited:

ksdroid

Member
Licensed User
on windows it works perfect i will try to compile it on ubuntu and will let you know the result
the same error with sample code so i don't think its my code
 

JakeBullet70

Well-Known Member
Licensed User
Hi @Roycefer

I have just starting using this lib and have run into the following.
upload_2016-5-12_17-56-26.png


I am using the latest lib

upload_2016-5-12_17-56-59.png


I could not find jna-4.2.0 and got the latested (4.2.2) and renamed 4.2.0 - I am on Windows and ANSI is set to false.
What am I doing wrong? Source is included.

Big thanks!!!
 

Attachments

  • Test.zip
    1.4 KB · Views: 170

JakeBullet70

Well-Known Member
Licensed User
Never mind... I am an idiot today!!! Just had to read post #6!!!
 

JakeBullet70

Well-Known Member
Licensed User
One more question... ;)

Anyway to test the console input in debug mode?

This sort of reminds me of my DBase days. All we need now are SAY's and GET's! LOL
 

Roycefer

Well-Known Member
Licensed User
I don't think there's any library-internal way to test console input in debug mode. I think you'll have to do something with
B4X:
#IF DEBUG
....
and read from a file or something when in debug mode. If my recollection serves me, debugging versions of programs all run in the main thread, which will break this library's threading model. Asynchronous console-input watching is performed in its own thread; this would block if performed in the main thread.
 
Top