11

I have several named tmux sessions. For example:

$ tmux new -s mysession1
$ tmux new -s mysession2

I would like to put something in my ~/.tmux.conf which would automatically log each session to a file in a directory like ~/mytmuxsessions/ accordingly to the session name. For example, given mysession1 and mysession2 created above, it would create the following log files, with the output of my terminal window commands (like the script -f file.log command does):

  1. ~/mytmuxsessions/mysession1.log
  2. ~/mytmuxsessions/mysession2.log

I tried researching it, and I found this:

  1. Is there an equivalent of GNU Screen's "log" command in tmux?
  2. https://blog.sleeplessbeastie.eu/2019/10/28/how-to-store-the-contents-of-tmux-pane/

These questions are trying to bind a key to a command, but I would like for it to be automatic, i.e., as something I can put on my ~/.tmux.conf and forget about it. For example, what this command seems to be doing it coping all my current output in a log file:

capture-pane -b temp-capture-buffer -S - \; save-buffer -b temp-capture-buffer ~/tmux.log \; delete-buffer -b capture-buffer

But what I would like would be something equivalent to Linux script command script -f mysession.log, which I can put into my ~/.tmux.conf to continually save my terminal output to a log file, accordingly to my session name.

1 Answer 1

4

I wasn't able to find a way to set the full file path to a logfile dynamically using just .tmux.conf. By the word, dynamically, I mean according to the session name in your case, and according to the Unix timestamp in my case. However, I was able to use the base code for tmux-logging; I hacked around with the source code to get something that works for me.

Note that this solution doesn't save everything from a particular session in one logfile. That could be done, but combining different windows and panes that could exist in such .a session would be difficult. However, this solution will save a logfile for each pane inside your session, so among the logfiles will be all your input and output. You could always fork from tmux-plugins/tmux-login, or you could write your own code based on what they've put together and use only what you like.


TL;DR

Install tmux-logging from the tmux-plugins git repo.

Change the variable definition for filename_suffix to match the format you'd like. This should be done in /path/to/tmux-plugins/tmux-logging/scripts/variables.sh.

Copy the toggle_logging.sh to another script - I called it ~/.ensure_tmux_logging_on.sh, and it is in my $HOME directory so a future update to tmux won't wipe it out. Change the script so that the if is_logging doesn't do anything (or just outputs a message). Leave the associated else branch as it is.

Call your ~/.ensure_tmux_logging_on.sh from your .bash_profile (or .bash_login or .login or .profile - whichever will make sure it gets called by non-login shells). tmux always opens a non-login shell, so .bashrc won't work unless you source it from one of those previous scripts. See here (1), here (2), or here (3) - all three being answers to the same AskUbuntu question - for more details.

Keep notes somewhere, because if an update comes for tmux-logging, you'll have to use the same hack.

I tried to figure out a way to do this with .tmux.conf, but had trouble getting the session information, because that seemed to be set in an accessible variable after .tmux.conf had been sourced.


More details

I used the Tmux Plugin Manager (tpm) method for installing tmux-logging, meaning I installed tpm as well as tmux-logging in one go. Here are some quick commands that do what's described at the READMEs of tpm and tmux-logging. Note, if you already have tpm, follow the instructions at the tmux-logging README, i.e. add the line

set -g @plugin 'tmux-plugins/tmux-logging'

to your list of plugins in ~/.tmux.conf.

You can find out if you have (at least the standard install of) tpm with this command

$ test ! -d ~/.tmux/plugins/tpm && \
 echo -e "\n\nYou don't have \`tpm', continue with the next commands\n" || \
 echo -e "\n\n\
You have \`tpm'.
\033[0;31mPAY ATTENTION TO THIS MESSAGE\x21\033[0m
Follow the instructions at
'https://github.com/tmux-plugins/tmux-logging/'
then
\033[0;32mgo to the 'After getting tmux-logging' part of the answer\033[0m\n"

If you don't have tpm, run

#(prompt can be in tmux or not)
## It's best, but not necessary, to get in the directory where we'll be doing stuff.
bbd025@MACHINE $ cd
## check the existence of ~/.tmux/plugins/tpm directory, 
##+ create it if it doesn't exist
bbd025@MACHINE $ test -d ~/.tmux/plugins/tpm || mkdir -p ~/.tmux/plugins/tpm
## clone in the tpm code
bbd025@MACHINE $ git clone https://github.com/tmux-plugins/tpm \
   ~/.tmux/plugins/tpm
## create ~/.tmux.conf if it doesn't exist
bbd025@MACHINE $ test -f ~/.tmux.conf || touch ~/.tmux.conf
## Add the lines to ~/.tmux.conf as directed by the READMEs. Note that
##+ that the lines with only a '.' allow for spacing between the end of
##+ anything currently in your ~/.tmux.conf and these lines
bbd025@MACHINE $ cat <<'EOF' | sed 's/^\.$//g;' >> ~/.tmux.conf
.
.
# List of plugins
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
# -v- OUR LOGGING PLUGIN -v-
set -g @plugin 'tmux-plugins/tmux-logging'

# Other examples:
# set -g @plugin 'github_username/plugin_name'
# set -g @plugin 'github_username/plugin_name#branch'
# set -g @plugin '[email protected]:user/plugin'
# set -g @plugin '[email protected]:user/plugin'

#Initialize TMUX plugin manager(keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm'
EOF
bbd025@MACHINE $

After getting tmux-logging

Note that if you've used another method to install tmux-logging or configured things differently, you'll need to use /my/path/to/tmux-logging/ in place of ~/.tmux/plugins/tmux-logging/.

If you haven't already done so, start a tmux session

$ tmux

You should see a green banner-type thingy on the bottom letting you know you're in tmux. Press [Prefix] + Shift + i (also called [Prefix] + I and, by default, Ctrl + b then Shift + i.)

It will show the following lines in the actual terminal, rather than in the green notification bar.

TMUX environment reloaded.
Done, press ENTER to continue.

Press ENTER

Now, let's edit ~/.tmux/plugins/tmux-logging/scripts/variables.sh.

For what is close to your (the OP's) desired outfile name (but which won't get overwritten or scrambled when a new window or pane is called up - at least that's what I guess would happen), change it so you have the following lines:

# General options
# Next, commented line has original variable
#replaced#filename_suffix="#{session_name}-#{window_index}-#{pane_index}-%Y%m%dT%H%M%S.log"
filename_suffix="#{session_name}-#{window_index}-#{pane_index}.log"

(Check out Note [1] to see how I keep this information safe in case of a tmux update.)

You could take out the -#{window_index}-#{pane_index} if you never open multiple windows or panes in your session. That would give a logfile name exactly matching that desired by the OP. If you do end up with multiple panes, I'm really not sure what would happen with the logging, but I think it would be messed up if not overwritten. FYI, the variables you're seeing there are coming from ~/.tmux/plugins/tmux-logging/scripts/shared.sh.

Note that you could also change the default_logging_path a few lines down the file, but this is something that can be taken care of in ~/.tmux.conf, so I didn't mess with it.

What I did - where I wanted my username to be part of the logfile, I wanted an absolutely unique and trackable timestamp, and I wanted things sorted chronologically (and where I don't like my files going over 80 characters per line) - was to change lines so I had

# General options
# -v- original before change by bballdave025 -v- , 2022-02-28
#filename_suffix=\
#"#{session_name}-#{window_index}-#{pane_index}-%Y%m%dT%H%M%S.log"
filename_suffix="bbd025-%s\
-#{session_name}-#{window_index}-#{pane_index}-%Y%m%dT%H%M%S%z.log"

If you don't want something like tmux- before your filename, you should make another change to the variables.sh file. I don't do this for my own machine, because there are different filename prefixes for slightly different logging options, but I'm doing it for this answer.

So, if you don't want any prefix, change variables.sh to have the lines:

# original commented out on the line below
#default_logging_filename="tmux-${filename_suffix}"
default_logging_filename="${filename_suffix}"

Now, let's add what we need to our ~/.tmux.conf. The first line will put output logs into the directory you specified. Any other legitimate path could be placed here. Let's make it so that, if you use the tmux-logging screen-capture and/or log-buffer-and-continue-logging options, the screencap or complete-history-with-buffer will be saved in the same directory with your auto-start logfiles. It shouldn't matter exactly where in your ~/.tmux.conf file you put these lines, though I keep it before that last line which TMUX Plugin Manager told us to keep at the end.

set -g @logging-path "$HOME/mytmuxsessions"
set -g @screen-capture-path "$HOME/mytmuxsessions"
set -g @save-complete-history-path "$HOME/mytmuxsessions"

Next, we'll copy toggle_logging.sh to a new script and make sure it is included in our ~/.bash_profile (or by whatever gets called for sure by non-login scripts.)

I actually source my ~/.bashrc from my ~/.bash_profile with the lines:

# @file  .bash_profile

if [ -f ~/.bashrc ]; then
  . ~/.bashrc
fi

If there are lines such as the following in your ~/.bashrc, you can comment them out.

# If not running interactively, don't do anything
[[ "$-" != *i* ]] && return

to

## If not running interactively, don't do anything
#[[ "$-" != *i* ]] && return

Otherwise, if you want to keep your ~/.bashrc for environment and functions ONLY for use in login shells, just add the lines I'll put at the end of ~/.bashrc at the end of ~/.bash_login, or whichever file gets loaded for non-login scripts.

Finally, then, we're getting to our copy of toggle_logging.sh. Run the copy command given below. Again, I put mine in my home folder (~, a.k.a. $HOME) to make sure it doesn't get wiped out by some future tmux upgrade. You can choose a directory and a filename that suits you rather than the ones chosen below.

$ cp ~/.tmux/plugins/tmux-logging/scripts/toggle_logging.sh \
     ~/.ensure_tmux_logging_on.sh

(If you didn't use the tpm option for tmux-logging, you'll need to get to that script from wherever you cloned or built your tmux-logging.)

Now, make changes to your ~/.ensure_tmux_logging_on.sh so they look like what's below. (Note that you don't need to keep in the original code that I have commented out. Those parts are there so you can search for the original text and either replace it or comment it out as I have. That way, you'll know where the new stuff should go.)

---

### changed for auto-logging (I often put in my username and date of change)
### bballdave025, 2022-02-28
#orig#CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

#orig#source "$CURRENT_DIR/variables.sh"
#orig#source "$CURRENT_DIR/shared.sh"

## new for tmux auto-logging, bballdave025 2022-02-28
TMUX_LOGGING_SCRIPTS_DIR="$HOME/.tmux/plugins/tmux-logging/scripts"
CURRENT_DIR="$TMUX_LOGGING_SCRIPTS_DIR"
  ## Not really our current dir, but it allows other code called
  ##+ downstream to work.

source "$TMUX_LOGGING_SCRIPTS_DIR/variables.sh"
source "$TMUX_LOGGING_SCRIPTS_DIR/shared.sh"

---

### original function commented out, bballdave025 2022-02-28
#stop_pipe_pane() {
#  tmux pipe-pane
#  display_message "Ended logging to $logging_full_filename"
#}

### new function added, bballdave025 2022-02-28
confirm_logging()
{
  display_message "Logging already happening to ${logging_full_filename}"
}

---

#original comment## starts/stop logging
# starts/continues logging ( bballdave025, 2022-02-28 )
#orig#toggle_pipe_pane() {
## -v- new function name -v- , bballdave025, 2022-03-09
ensure_pipe_pane_on()
{
  if is_logging; then
    #orig#set_logging_variable "not logging"
    #orig#stop_pipe_pane
    # -v- bballdave025 2022-02-28 -v- first line probably unneeded (?)
    set_logging_variable "logging"
    confirm_logging
    # everything from here is as in the original
  else
    set_logging_variable "logging"
    start_pipe_pane
  fi
}

## -v- main changed -v- by bballdave025, 2022-03-08
#orig#main() {
#orig#  if supported_tmux_version_ok; then
#orig#    toggle_pipe_pane
#orig#  fi
#orig#}

main()
{
  if supported_tmux_version_ok; then
    ensure_pipe_pane_on
  fi
}
main

---

You don't need to be as verbose as I was, i.e. you don't need all the comments and old lines. I'm hoping to keep things clear as to what is being changed. There are ways to make this code simpler; if you understand what's going on, you're welcome to do so.

Make sure your script is executable

$ chmod +x ~/.ensure_tmux_logging_on.sh

Now, we have a few lines to add this to whichever file - ~/.bash_profile, a ~/.bashrc that gets sourced by ~/.bash_profile etc. - that you know will get called for non-login terminals. In my example, I made it so ~/.bashrc got called by `~/.bash_profile for non-login screens, so I'm going to add my lines there.

Having these lines will make it so ~/.ensure_tmux_logging_on.sh gets run every time we go into a new tmux terminal for the first time. Simply add the following lines just about anywhere in your ~/.bashrc (or other file); I put it towards the end.

if [ ! -z $TMUX ]; then
  $HOME/.ensure_tmux_logging_on.sh
fi

Test it out on my system, `RHEL 8` (RedHat based).

Short version: it works. You can skip all this next part, though I do suggest looking at the 5 items just below.

The important things to know are these:

1) Now, running the OP's commands, i.e.

$ tmux new -s mysession1

leads to the logfile,

~/mytmuxsessions/mysession1-1-1.log

And similary, $ tmux new -s mysession2 leads to the logfile, ~/mytmuxsessions/mysession2-1-1.log

2) Any newly-created windows/panes in these sessions will be denoted by numbers, even if you create them with names using commands at the terminal prompt.

3) If you don't provide a name for the session (which providing of a name can only be done by starting a new window from the terminal prompt and using the (   -s   ) SESSION_NAME` option, you will only get the assigned session number.

4) As things are set up for the OP, a log for any new session having the same name as one created before, e.g. a second use of tmux new -s mysession1 will overwrite the previous logfile. That doesn't feel very clear without an example, so here is one.

$ ls -lAh ~/mytmuxsessions # to see that mysession1-1-1.log is already there
-rw-r--r--. 1 bbd025 bbd025 405 Mar   1  10:22 mysession1-1-1.log
-rw-r--r--. 1 bbd025 bbd025 824 Feb   28 17:40 mysession2-1-1.log
$ tmux new -s mysession1 # I'll enter a new tmux session after this command
(tmux)$ # Some stuff.
(tmux)$ echo "Putting text, I/O, etc. in the logfile"
(tmux)$ alias ls='ls --color=auto'
(tmux)$ type ls
ls is aliased to 'ls --color=auto'
(tmux)$ tmux kill-session -t mysession1
$ ls -lAh ~/mytmuxsessions
-rw-r--r--. 1 bbd025 bbd025 1.6K Mar   2  13:02 mysession1-1-1.log
-rw-r--r--. 1 bbd025 bbd025 824  Feb   28 11:40 mysession2-1-1.log

The first logfile, mytmuxsession1-1-1.log, created Mar 1 10:22, and having a size of 405 bytes no longer exists. Instead, we now have mytmuxsession1-1-1.log, created Mar 2 13:02, and having a size of 1.6K.

If you want to avoid this overwriting, just include some type of date string, in the format of the BSD date command, e.g. change your tmux-logging's variables.sh to have something like

filename_suffix="#{session_name}-#{window_index}-#{pane_index}\
-%Y%m%dT%H%M%S%z.log"

Note that this is different from the overwriting during a tmux session that I discussed before.

5) I'm not sure if this is actually necessary, but I added the following to my ~/.bashrc (or ~/.bash_profile or whatever). There is a version with more comments/options further down in the example.

## Function to be run every time one exits a bash terminal
finish()
{
  if [ ! -z $TMUX  ]; then
    $HOME/.tmux/plugins/tmux-logging/scripts/toggle_logging.sh
  fi
}

trap finish EXIT

Short Example

Before you test things out, you should exit and restart your terminal, or run source ~/.bashrc (or source whichever similar file you edited). I prefer the second method, but if you're not sure about the ~/.bash_profile, ~/bashrc, etc. stuff, it might be best for you just to restart.

Edit I had a longer example which felt even more like overkill than this answer already did. If you really want to see it, I've taken screenshots which will be attached. For now, the pages - p1, p2, and p3 - now all point to www.google.com. p1, p2, and p3.

Instead of the long example, I'm just going to show a sequence of commands. For my real logs, I ran a few commands and made a few comments after getting into a new tmux session, window, or pane. At the end, I'll show the resultant files.

I should be a bit more specific about my system. I've got an SSH connection through PuTTY to a RHEL8 machine, which gives me a main terminal. From there, I run

$ tmux new -s mysession1

(This will bring up session: mysession1; window: 1; and pane: 1. It will also start the logfile mysession1-1-1.log.)

After doing a few comments and pressing [Prefix]+[w], this is what the tmux terminal looks like.

┌──────────────────────────────────────────────────────────────────────────────┐
│(0)  - mysession1: 1 windows (attached)                                       │
│(1)  └─> 1 bash* (1 panes) "bbd025@MACHINE: /home/bbd025"                     ┆
│
│
│┌ 1 (sort: index)────────────────────────────────────────────────────────────┐┆
││ bbd025@MACHINE:~                                                           ││
││ $ # I'm in tmux, and it looks like it's logging!                           ││
││ bbd025@MACHINE:~                                                           ││
││ $ # I'll press [Prefix]+[w] to see session info & then [ENTER] to get back ││
││ bbd025@MACHINE:~                                                           ││
││                                 ┌───┐                                      ││
││                                 │ 1 │                                      ││
││                                 └───┘                                      ││
│└────────────────────────────────────────────────────────────────────────────┘│
│[mysession1:bash*                  "bbd025@MACHINE: /home/bbd" HH:MM dd-mmm-yy│
└──────────────────────────────────────────────────────────────────────────────┘

[Prefix]+w brought up what you see above. To get back to the session, make sure it's highlighted (it's the only session, so you shouldn't have to do anythingq), and press ENTER.

Let's detach the session and get started with mysession2. Sorry that I messed up from the command line, but I want the sizes of the logfiles to be correct

$ tmux detach-client -s mysession1

Now, we're back in the non-tmux shell. Though I show a different code block, it replaces what was on the terminal while still in the same terminal location.

$ tmux new -s mysession2

The tmux terminal that comes up will have logfile mysession2-1-1.log

At this point, using [Prefix]+w brings up

(0)  - mysession1: 1 windows
(1)  └─> 1: bash* (1 panes) "bbd025@MACHINE: /home/bbd025"
(2)  - mysession2: 1 windows (attached)
(3)  └─> 1: bash* (1 panes) "bbd025@MACHINE: /home/bbd025"
$ tmux detach-client -s mysession2

brings us back to the non-tmux shell, where we can kill mysession2.

$ tmux kill-session -t mysession2

Let's try opening another window in the mysession1, then a separate pane from the second window, and see how the logs end up. Starting from the non-tmux terminal.

tmux attach -t mysession1

You'll see whatever you've already typed in the original tmux new -s mysession1 pane.

$ tmux new-window -n second_window

A new, blank tmux bash terminal comes up. This will have logfile mysession1-2-1.log.

$ tmux ls
mysession1: 2 windows (Created Mon Feb  28 12:02:06 2022) [80x29] (attached)
$ # I press [Prefix]+[n] to get to the next `tmux` window, i.e. the one with
$ #+ logfile, `mysession1-1-1.log`
$ tmux split-window -v # This is the same as [Prefix]+[%]

There are now two terminals - separated by a line of dashes - one on top of the other. The bottom terminal has logfile mysession1.1.2

$ tmux display-panes
$ # You'll have seen numbers on each pane. The bottom one should have
$ #+ shown '2'. If you want to make sure you are typing in pane 2, do
$ tmux select-pane -t 2
$ # You could also have done [Prefix]+[q]+[2]

From pane 2, let's open a new window without a name.

$ tmux new -w

You're now in a tmux terminal with logfile 0-1-1.

Now, after a tmux kill-server, let's do a final check on the logfiles.

# In T0
$ ls -Ss1pq --block-size=1
total 28672
12288 mysession1-1-1.log
 4096 0-1-1.log
 4096 mysession1-1-2.log
 4096 mysession2-1-1.log
 4096 mysession1-2-1.log
$ ls -lAh
total 28K
-rw-r--r--. 1 bbd025 bbd025 1.3K Mar  8 12:03 0-1-1.log
-rw-r--r--. 1 bbd025 bbd025 8.5K Mar  8 11:49 mysession1-1-1.log
-rw-r--r--. 1 bbd025 bbd025  767 Mar  8 11:49 mysession1-1-2.log
-rw-r--r--. 1 bbd025 bbd025  648 Mar  8 11:49 mysession1-2-1.log
-rw-r--r--. 1 bbd025 bbd025  725 Mar  7 17:41 mysession2-1-1.log

Notes:

[1] Our new filename_suffix variable likely wouldn't survive a tmux update. So that I can remember and have access to the changes, I would (in the case of the OP) add the following to my ~/.bash_profile

export MY_TMUX_FNAME_SUFFIX_LINE='filename_suffix="#{session_name}-#{window_index}-#{pane_index}.log"'

For my case, I would add the following. I'm skipping my 80-character-per-line rule to make things simpler.

export MY_TMUX_FNAME_SUFFIX_LINE='filename_suffix="bbd025-%s-#{session_name}-#{window_index}-#{pane_index}-%Y%m%dT%H%M%S%z.log"'

Now, you basically have an environment variable - visible with

echo $MY_TMUX_FNAME_SUFFIX_LINE

which you can look back to after a future tmux update, so you know how to keep the personalization you've made for your logfile name.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .