#! /usr/bin/env bash set -euo pipefail #### Detect Toolkit Project Root #### # if realpath is not available, create a semi-equivalent function command -v realpath >/dev/null 2>&1 || realpath() { [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" } SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")" SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" TOOLKIT_ROOT="$(realpath "$SCRIPT_DIR/..")" if [[ ! -d "$TOOLKIT_ROOT/bin" ]] || [[ ! -d "$TOOLKIT_ROOT/config" ]]; then echo "ERROR: could not find root of overleaf-toolkit project (inferred project root as '$TOOLKIT_ROOT')" exit 1 fi DEFAULT_TAIL_LINES=20 ALL_SERVICES=(chat clsi contacts docstore document-updater filestore git-bridge \ mongo notifications real-time redis spelling tags track-changes web \ history-v1 project-history) LOGS_PID_FILE="/tmp/toolkit-logs.$$.pid" function usage() { echo "Usage: bin/logs [OPTIONS] [SERVICES...] Services: chat, clsi, contacts, docstore, document-updater, filestore, git-bridge, mongo, notifications, real-time, redis, spelling, tags, track-changes, web, history-v1, project-history Options: -f follow log output -n {number} number of lines to print (default $DEFAULT_TAIL_LINES) use '-n all' to show all log lines Examples: bin/logs -n 50 web clsi bin/logs -n all web > web.log bin/logs -f web bin/logs -f web chat docstore bin/logs -n 100 -f filestore bin/logs -f" } function parse_args() { TAIL_LINES=$DEFAULT_TAIL_LINES FOLLOW=false if [[ $# -gt 0 && $1 =~ ^help|-h|--help$ ]]; then usage && exit fi while getopts "fn:" opt do case $opt in f) FOLLOW=true ;; n) TAIL_LINES="$OPTARG" ;; \?) usage && exit 1;; esac done shift $(( OPTIND - 1 )) if [[ $# -eq 0 ]]; then SERVICES=("${ALL_SERVICES[@]}") else SERVICES=("$@") fi } function docker_compose() { # Ignore stderr and errors when calling docker compose "$TOOLKIT_ROOT/bin/docker-compose" "$@" 2>/dev/null || true } function kill_background_jobs() { if [[ ${#COMPOSE_LOGS_PIDS[@]} -gt 0 ]]; then # Kill "docker compose logs" processes kill "${COMPOSE_LOGS_PIDS[@]}" 2>/dev/null || true fi # Kill "tail -f" processes started inside the sharelatex container docker_compose exec -T sharelatex bash -c " [[ -f $LOGS_PID_FILE ]] && kill \$(cat $LOGS_PID_FILE) 2>/dev/null rm -f $LOGS_PID_FILE" } function show_logs() { COMPOSE_LOGS_PIDS=() trap kill_background_jobs EXIT for service in "${SERVICES[@]}"; do if [[ $service =~ ^(git-bridge|mongo|redis)$ ]]; then show_compose_logs "$service" & COMPOSE_LOGS_PIDS+=($!) else show_sharelatex_logs "$service" & fi done wait } function show_compose_logs() { local service="$1" local flags=(--no-color) if [[ $FOLLOW == "true" ]]; then flags+=(-f) fi if [[ $TAIL_LINES != "all" ]]; then flags+=(--tail="$TAIL_LINES") fi if [[ ${#SERVICES[@]} -eq 1 ]]; then # Do not add a service prefix when outputting logs from a single # service flags+=(--no-log-prefix) fi docker_compose logs "${flags[@]}" "$service" } function show_sharelatex_logs() { local service="$1" local log_path="/var/log/sharelatex/$service.log" local flags=() if [[ $FOLLOW == "true" ]]; then flags+=(-f) fi if [[ $TAIL_LINES = "all" ]]; then flags+=(-n "+1") else flags+=(-n "$TAIL_LINES") fi local logs_cmd="[[ -f $log_path ]] && echo \$\$ >> $LOGS_PID_FILE && tail --pid=\$\$ ${flags[*]} $log_path" if [[ ${#SERVICES[@]} -gt 1 ]]; then # Roughly reproduce the service prefix format from docker compose local padded_service padded_service=$(printf "%-13s" "$service") logs_cmd="$logs_cmd | sed -u -e 's/^/$padded_service | /'" fi docker_compose exec -T sharelatex bash -c "$logs_cmd" } parse_args "$@" show_logs