B4J Library jColorLogger - colorful console and console input

Discussion in 'B4J Libraries & Classes' started by Roycefer, Jul 9, 2015.

  1. Roycefer

    Roycefer Well-Known Member Licensed User

    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:
    Code:
    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.
     

    Attached Files:

    Last edited: Oct 24, 2015
  2. thader2012

    thader2012 Member Licensed User

    Oh! it's very nice. Thank you.
     
  3. Roycefer

    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:
    Code:
    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: Jul 18, 2015
  4. Roycefer

    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:
    Code:
    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:
    Code:
    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: Jul 18, 2015
  5. Roycefer

    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.
    Code:
    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: Oct 24, 2015
    Erel likes this.
  6. Roycefer

    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
    Code:
    GetConsoleInput
    method counter-intuitive so I've made some changes in version 1.31.
    Code:
    GetConsoleInput
    is now
    Code:
    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
    Code:
    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
    Code:
    GetConsoleInputLineAsync
    .

    Also, you should only have one
    Code:
    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.
     
  7. Roycefer

    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.
    Code:
    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, 011False) & "%"
        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-3Then
                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)/101,2,2False), "aqua""dark_blue").ColorLogWord(" MB/s""""")
    End Sub
    It produces output that looks like this:
    pb.gif
     
    Last edited: Aug 15, 2015
  8. giga

    giga Well-Known Member Licensed User

  9. Roycefer

    Roycefer Well-Known Member Licensed User

    I fixed a minor bug in version 1.5. The library behaves better, now, on start up.
     
  10. ksdroid

    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?
     
  11. Roycefer

    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.
     
  12. ksdroid

    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)
     
  13. Roycefer

    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?
     
  14. ksdroid

    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
     
  15. Roycefer

    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: Feb 3, 2016
    inakigarm likes this.
  16. ksdroid

    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
     
  17. JakeBullet70

    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!!!
     

    Attached Files:

  18. JakeBullet70

    JakeBullet70 Well-Known Member Licensed User

    Never mind... I am an idiot today!!! Just had to read post #6!!!
     
  19. JakeBullet70

    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
     
  20. Roycefer

    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
    Code:
    #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.
     
    JakeBullet70 likes this.
Loading...