Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider adding colored output or prepended string for each line of output #54

Open
borkdude opened this issue Feb 10, 2022 · 9 comments

Comments

@borkdude
Copy link
Contributor

E.g.:

(process ["foo" "bar"] {:color :green :prepend "FOO"})

This would be useful in babashka tasks where multiple processes are running in parallel so you can distinguish them.

@jkxyz
Copy link

jkxyz commented Feb 10, 2022

Just a note that foreman only colors the process name at the beginning of the line. The rest of the output is unmodified.

Would it be sufficient to just have a convenience option like

:process-line-fn (fn [s] (str (termcolor :green "FOO | ") s))

@teodorlu
Copy link

Pasting in my current plain bash solution that I'd like to replace. Essentially, I'm wrapping each line with some stuff.

frontend and backend are normal bash functions that cd into the right directory, and run make dev or something similar.

Perhaps I could delete the whole thing and replace it with a little bb.edn :)

with_prefix() {
    # Usage: with_prefix $APPNAME $COLOR
    local NOCOLOR='\033[0m'
    awk -v name="$1" -v color="$2" -v nocolor="$NOCOLOR" '{ printf "%s[%s]%s %s\n", color, name, nocolor, $0 }'
}

apps() {
    # from https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux
    #
    #     Black        0;30     Dark Gray     1;30
    #     Red          0;31     Light Red     1;31
    #     Green        0;32     Light Green   1;32
    #     Brown/Orange 0;33     Yellow        1;33
    #     Blue         0;34     Light Blue    1;34
    #     Purple       0;35     Light Purple  1;35
    #     Cyan         0;36     Light Cyan    1;36
    #     Light Gray   0;37     White         1;37

    local NO_COLOR='\033[0m'

    local BLACK='\033[0;30m'
    local RED='\033[0;31m'
    local GREEN='\033[0;32m'
    local BROWN_ORANGE='\033[0;33m'
    local BLUE='\033[0;34m'
    local PURPLE='\033[0;35m'
    local CYAN='\033[0;36m'
    local LIGHT_GRAY='\033[0;37m'
    local DARK_GRAY='\033[1;30m'
    local LIGHT_RED='\033[1;31m'
    local LIGHT_GREEN='\033[1;32m'
    local YELLOW='\033[1;33m'
    local LIGHT_BLUE='\033[1;34m'
    local LIGHT_PURPLE='\033[1;35m'
    local LIGHT_CYAN='\033[1;36m'
    local WHITE='\033[1;37m'
    local BLACK='\033[0;30m'
    local RED='\033[0;31m'
    local GREEN='\033[0;32m'

    # If secrets env vars are not set, try to source bash / zsh.

    frontend 2> >(with_prefix "frontend" "$RED") 1> >(with_prefix "frontend" "$GREEN")  &
    backend  2> >(with_prefix "backend" "$RED")  1> >(with_prefix "backend" "$BLUE")    &
    nats     2> >(with_prefix "nats" "$RED")     1> >(with_prefix "nats" "$LIGHT_CYAN") &
    sleep 999999999999
}

@borkdude
Copy link
Contributor Author

@jkxyz Indeed, having just a function to map over the stdout and/or stderr would be the most flexible solution. Just an interceptor basically.

@borkdude
Copy link
Contributor Author

borkdude commented Feb 8, 2023

See discussion here with a potential solution:

#102 (reply in thread)

@borkdude
Copy link
Contributor Author

I'm posting this little script here which may form a basis for the implementation:

(ns proc-wrapper
  "Taken from: https://github.com/babashka/nbb/blob/proc-wrapper/script/proc_wrapper.clj"
  (:require
    [babashka.process :as process :refer [process]]
    [clojure.string :as str]))

(def output-lock (Object.))

(defn output-wrapper
  [stream prefix writer]
  (let [buf (byte-array 1024)
        buffer (atom nil)]
    (loop []
      (let [read (.read stream buf)]
        (when-not (= -1 read)
          (let [s (String. buf 0 read)
                last-newline (str/last-index-of s \newline)
                before-last-newline (when last-newline (subs s 0 last-newline))
                after-last-newline (if last-newline
                                     (subs s (inc last-newline))
                                     s)]
            (when before-last-newline
              (let [buffered @buffer
                    _ (reset! buffer nil)
                    lines (str/split-lines (str buffered before-last-newline))]
                (doseq [l lines]
                  (locking output-lock
                    (binding [*out* writer]
                      (println prefix l))))))
            ;; (Thread/sleep (rand-int 100))
            (swap! buffer (fn [buffer]
                            (if after-last-newline
                              (str buffer after-last-newline)
                              buffer)))
            (recur)))))))

(defn green [s]
  (format "�[32m%s�[39m" s))

(defn red [s]
  (format "�[31m%s�[39m" s))

(defn wrap
  [prefix args]
  (let [proc (apply process args)
        output-out (future (output-wrapper (:out proc) prefix *out*))
        output-err (future (output-wrapper (:err proc) prefix *err*))
        #_#_checked (process/check proc)]))

(wrap (green "[0] ") ["bb -e '(loop [] (Thread/sleep 100) (println \"yes\") (recur))"])
(wrap (red "[1] ") ["bb -e '(loop [] (Thread/sleep 100) (println \"no\") (recur))"])

@(promise)

@borkdude
Copy link
Contributor Author

Added a branch: https://github.com/babashka/process/tree/out-line-fn
where process has another two options: :out-line-fn and :err-line-fn which is a callback on lines of text

@borkdude
Copy link
Contributor Author

So I was looking into improving bb tasks parallel output with a prefix per process. I added a new option to babashka.process called :out-line-fn and :err-line-fn and function called concurrently (named after the npm util) which uses these new options.
It looks like this, combined (tested in the squint bb.edn)
It works great, but the only problem is: when doing a :out-line-fn (to prefix the output with a number), the process notices it doesn't write directly to a tty so it doesn't give any colored output anymore :(

Screenshot 2024-10-19 at 14 25 37 Screenshot 2024-10-19 at 14 24 37 Screenshot 2024-10-19 at 14 24 09

@borkdude
Copy link
Contributor Author

It seems like we can use:

https://force-color.org/

@borkdude
Copy link
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants