Is possible to use Observer with multiple applications?

I am playing around with Observer, and I am able to connect to any application I have running on the same docker network.

I start the application from each docker container like:

elixir --cookie super-long-secret-cookie --sname app1 -S mix phoenix.server

and I start the observer from another docker container with:

erl -sname observer -hidden -setcookie super-long-secret-cookie  -run observer

In the Observer GUI interface I am able to connect to app1 and app2 without any problem, and see them working as expected, but each time I need to swap between app1 and app2 I need to reenter the de node details to reconnect, because in the Nodes menu it does not keep a list of all apps I am connecting to during the current session…

20190413_192359_1

So my expectation is to see a list of all nodes I have already connected to, and be able to click in any to reconnect to it, but as we can see in the image I only have the observer node itself, and none of the nodes of the app I have already connected to in this Observer session… is this the actual behavior of Observer or is just me having an issue with it?

I found the solution…

I was starting Observer with the Erlang shell instead of the Elixir shell.

So instead of using:

erl -sname observer -hidden -setcookie super-long-secret-cookie  -run observer

I need to use:

iex --sname observeriex --cookie super-long-secret-cookie -e ":observer.start"
2 Likes

Actually it was because you were using -hidden on the initial call, that means it doesn’t join the BEAM mesh (so you only see the thing you explicitly named and nothing else) but rather only joins that specific node. :slight_smile:

2 Likes

Good catch, but still not working with:

erl -sname observer -setcookie super-long-secret-cookie  -run observer

How is it not working? You aren’t connected to a mesh network yet at that point, need to do that before observer launches as I recall. :slight_smile:

Thanks to point it out… I indeed forgot to connect to the node…

So the correct command is indeed:

iex --name 'observer@172.24.0.3'  --cookie 'super-long-secret-cookie' -e 'Node.connect(:"node_name@172.24.0.2")' -e ":observer.start()"

I am still learning all this stuff, and at same toime building a Docker stack for development in Elixir with all the tooling available.

2 Likes

So that worked all for you? Awesome! :slight_smile:

Well the weird part is that if I put the command in a bash script with a variable to replace the node connect bit it will not work, but works fine if is only plain text…

#!/bin/bash

set -eu

Main()
{
  ##############################################################################
  # INPUT
  ##############################################################################

    if [ "$#" -ge 1 ]; then
        local node_name="${1? Missing node name!!!}"
        local node_address="${2? Missing node address!!!}"
        local node_cookie="${3? Missing node cookie!!!}"

        local app_node_name="${node_name}@${node_address}"
    else
        # GET FROM ENVIRONMENT WHEN NO ARGUMENTS ARE PASSED TO THE SCRIPT
        app_node_name="${APP_NODE_NAME}"
        node_cookie="${APP_NODE_COOKIE}"
    fi

    local observer_node_name="observer@$(hostname -i)"


  ##############################################################################
  # EXECUTION
  ##############################################################################

    node_connect="Node.connect(:\"${APP_NODE_NAME}\")"

    # DOES NOT CONNECT THE NODE: -e "${node_connect}"
    iex --name "${observer_node_name}"  --cookie "${node_cookie}" -e "${node_connect}" -e ":observer.start()"

    # BUT THIS WORKS FINE FROM INSIDE THIS SCRIPT OR EXECUTED DIRECTLY FROM A TERMINAL
    iex --name 'observer@172.24.0.3'  --cookie 'super-long-secret-cookie' -e 'Node.connect(:"node_name@172.24.0.2")' -e ":observer.start()"
}

Main "${@}"
1 Like

Hmm, put echo before your iex --name "${observer_node_name}" --cookie "${node_cookie}" -e "${node_connect}" -e ":observer.start()" line to become echo iex --name "${observer_node_name}" --cookie "${node_cookie}" -e "${node_connect}" -e ":observer.start()" and see what it prints out and if everything is appearing properly interpolated and matching your working line (excepting "'s and so forth of course)? Are you sure the shell is not interpolating out your "'s in the :"node_name@blah" part and so forth? I don’t quite remember the rules of bash in interpolation but I know the way it interpolates is rather broken so it wouldn’t surprise me (I use ZSH, which is basically bash done right, though I keep my scripts to full posix shell mode regardless).

2 Likes

Bingo you found it… It was splinting the command into a new line… I was not able to found how the string end ups with the new line, but I fixed it before I build the command with:

# REMOVING UNWANTED STUFF
    local app_node_name="$(echo ${app_node_name} | tr -d '\n\r\t')"

So the full working script is now:

#!/bin/bash

set -eu

Main()
{
  ##############################################################################
  # INPUT
  ##############################################################################

    if [ "$#" -ge 1 ]; then
        local node_name="${1? Missing node name!!!}"
        local node_address="${2? Missing node address!!!}"
        local node_cookie="${3? Missing node cookie!!!}"

        local app_node_name="${node_name}@${node_address}"
    else
        # GET FROM ENVIRONMENT WHEN NO ARGUMENTS ARE PASSED TO THE SCRIPT
        app_node_name="${APP_NODE_NAME}"
        node_cookie="${APP_NODE_COOKIE}"
    fi

    local observer_node_name="observer@$(hostname -i)"


  ##############################################################################
  # EXECUTION
  ##############################################################################

    # REMOVING UNWANTED STUFF
    local app_node_name="$(echo ${app_node_name} | tr -d '\n\r\t')"

    local node_connect="Node.connect(:\"${app_node_name}\")"

    iex --name "${observer_node_name}"  --cookie "${node_cookie}" -e "${node_connect}" -e ":observer.start()"

}

Main "${@}"

My default shell is also zsh with oh-my-zsh plugin, but scripting I do in sh by default and resort to bash only when I really need a bash-ism :wink:

3 Likes

That tells me that there was bad input on the commandline or an an env-var, which you could fix by ‘removing’ the appropriate "'s around them (you really only need to keep those if whitespace or newlines are important, otherwise by not having it then it will collapse to a single line with single spaces instead of repeated spaces).

1 Like

So turns out the culprit was this:

 ENV_VAR=$(hostname -i)

Needs to be:

 ENV_VAR=$(echo -n $(hostname -i))

Feel free to suggested a cleaner way of doing it…

Maybe:

ENV_VAR=$(hostname -i | tr -d '\n')

That should work just fine, trailing newlines are not kept… o.O

╰─➤  ENV_VAR=$(hostname -i)

╰─➤  ENV_VAR1=$(echo -n $(hostname -i))

╰─➤  [ "${ENV_VAR}" = "${ENV_VAR1}" ] && echo they are identical || echo they are not identical
they are identical

So… that’s weird… o.O

You need to save the output into a file to see the new line and see the file in a text editor, cat will not show the new line :wink:

With new line in the file:

$ hostname -i > out.txt
$ echo $(hostname -i) > out2.txt

Without a new line in the file:

$ echo -n $(hostname -i) > out3.txt

$ hostname -i | tr -d '\n' > out4.txt

My shell

$ echo $SHELL
/usr/bin/zsh

$ echo $ZSH
/home/exadra37/.oh-my-zsh

That’s saving to a file though, which would have a newline. The thing about a variable in bash/zsh is that it doesn’t keep the newline because newlines are part of $IFS (well, unless you changed $IFS, which would break a lot of things), so I’m still thinking a " is in use somewhere where it shouldn’t be (which inhibits the this)?

For note though, replacing a newline via calling another program can be slow in a loop, so you’d probably want to use shell-string-replacement instead (like NR='\r' then %{blahvar%$NL}), but if not in a tight loop it doesn’t matter. ^.^

Still though:

╰─➤  BLAH=$(hostname -i) 

╰─➤  printf ">${BLAH}<"
>127.0.1.1<% 

╰─➤  printf ">${BLAH}<\n"
>127.0.1.1<

# Now of course streaming the variable into a program will include the newline:
╰─➤  wc <<< "$BLAH"
 1  1 10

# But passing it as an argument will not:
╰─➤  perl -e 'print $ARGV[0]; print " -> "; print length($ARGV[0])' $BLAH
127.0.1.1 -> 9%

╰─➤  python3 -c 'import sys; print(repr(sys.argv)); print(len(sys.argv[1]))' $BLAH
['-c', '127.0.1.1']
9

╰─➤  elixir -e 'System.argv() |> IO.inspect(); System.argv() |> hd() |> String.length() |> IO.inspect()' $BLAH
["127.0.1.1"]
9

So I think I’m still not seeing enough of your script to see the issue, like where and how the ENV_VAR is used… ^.^;