Browse code

Merge pull request #6637 from stevekuznetsov/skuznets/logging

Merged by openshift-bot

OpenShift Bot authored on 2016/01/20 17:12:43
Showing 10 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,231 @@
0
+#!/bin/bash
1
+#
2
+# This library holds all of the logging functions for OpenShift bash scripts.
3
+
4
+# os::log::install_system_logger_cleanup installs os::log::clean_up_logger as a trap on interrupts, termination, and exit.
5
+# If any traps are currently set for these signals, os::log::clean_up_logger is prefixed.
6
+#
7
+# Globals:
8
+#  None
9
+# Arguments:
10
+#  None
11
+# Returns:
12
+#  None
13
+function os::log::install_system_logger_cleanup() {
14
+    trap "os::log::clean_up_logger; $(trap -p EXIT | awk -F"'" '{print $2}')" EXIT
15
+}
16
+
17
+# os::log::clean_up_logger should be trapped so that it can stop the logging utility once the script that
18
+# installed it is finished.
19
+# This function stops logging and generates plots of data for easy consumption.
20
+#
21
+# Globals:
22
+#  - LOG_DIR
23
+#  - LOGGER_PID
24
+#  - SAR_LOGFILE
25
+# Arguments:
26
+#  None
27
+# Returns:  
28
+#  None
29
+function os::log::clean_up_logger() {
30
+    local return_code
31
+    return_code=$?
32
+
33
+    # we don't want failures in this logger to 
34
+    set +o errexit
35
+
36
+    if jobs -pr | grep -q "${LOGGER_PID}"; then
37
+        kill -SIGTERM "${LOGGER_PID}"
38
+        # give logger ten seconds to gracefully exit before killing it
39
+        for (( i = 0; i < 10; i++ )); do
40
+            if ! jobs -pr | grep -q "${LOGGER_PID}"; then
41
+                # the logger has shutdown, we don't need to wait on it any longer
42
+                break
43
+            fi
44
+        done
45
+
46
+        if jobs -pr | grep -q "${LOGGER_PID}"; then
47
+            # the logger has not shutdown, so kill it
48
+            kill -SIGKILL "${LOGGER_PID}"
49
+        fi
50
+    fi
51
+
52
+    if ! which sadf  >/dev/null 2>&1; then
53
+        echo "[WARNING] System logger data could not be unpacked and graphed, 'sadf' binary not found in this environment."
54
+        return 0
55
+    fi
56
+
57
+    local log_subset_flags=( "-b" "-B" "-u ALL" "-q" "-r" )
58
+
59
+    local log_subset_names=( "iops" "paging" "cpu" "queue" "memory" )
60
+
61
+    local log_subset_file
62
+    local i
63
+    for (( i = 0; i < "${#log_subset_flags[@]}"; i++ )); do
64
+        log_subset_file="${LOG_DIR}/${log_subset_names[$i]}.txt"
65
+        # use sadf utility to extract data into easily-parseable format
66
+        sadf -d "${SAR_LOGFILE}" -- ${log_subset_flags[$i]} > "${log_subset_file}"
67
+
68
+        local ignored_columns="hostname,interval,"
69
+
70
+        # special cases for special output from SAR, because the tool often gives us columns full of baloney
71
+        if [[ "${log_subset_names[$i]}" == "cpu" ]]; then
72
+            ignored_columns="${ignored_columns}CPU,"
73
+        fi
74
+
75
+        os::log::internal::prune_datafile "${log_subset_file}" "${ignored_columns}"
76
+        os::log::internal::plot "${log_subset_file}"
77
+    done
78
+
79
+    set -o errexit
80
+
81
+    return "${return_code}"
82
+}
83
+
84
+# os::log::internal::prune_datafile removes the given columns from a datafile created by 'sadf -d'
85
+#
86
+# Globals:
87
+#  None
88
+# Arguments:
89
+#  - 1: datafile
90
+#  - 2: comma-delimited columns to remove, with trailing comma 
91
+# Returns:
92
+#  None
93
+function os::log::internal::prune_datafile() {
94
+    local datafile=$1
95
+    local column_names=$2
96
+
97
+    if [[ "${#column_names}" -eq 0 ]]; then
98
+        return 0
99
+    fi
100
+
101
+    local columns_in_order
102
+    columns_in_order=( $( head -n 1 "${datafile}" | sed 's/^# //g' | tr ';' ' ' ) )
103
+
104
+    local columns_to_keep
105
+    local i
106
+    for (( i = 0; i < "${#columns_in_order[@]}"; i++ )); do
107
+        if ! echo "${column_names}" | grep -q "${columns_in_order[$i]},"; then
108
+            # this is a column we need to keep, adding one as 'cut' is 1-indexed
109
+            columns_to_keep+=( "$(( $i + 1 ))" )
110
+        fi
111
+    done
112
+
113
+    # for the proper flag format for 'cut', we join the list delimiting with commas
114
+    columns_to_keep="$( IFS=','; echo "${columns_to_keep[*]}" )"
115
+
116
+    cut --delimiter=';' -f"${columns_to_keep}" "${datafile}" > "${datafile}.tmp"
117
+    sed -i '1s/^/# /' "${datafile}.tmp"
118
+    mv "${datafile}.tmp" "${datafile}"
119
+}
120
+
121
+# os::log::internal::plot uses gnuplot to make a plot of some data across time points. This function is intended to be used 
122
+# on the output of a 'sar -f' read of a sar binary file. Plots will be made of all columns and stacked on each other with one x axis.
123
+# This function needs the non-data columns of the file to be prefixed with comments. 
124
+#
125
+# Globals:
126
+#  - LOG_DIR
127
+# Arguments:
128
+#  - 1: data file
129
+# Returns:
130
+#  None
131
+function os::log::internal::plot() {
132
+    local datafile=$1
133
+    local plotname
134
+    plotname="$(basename "${datafile}" .txt)"
135
+
136
+    # we are expecting the output of a 'sadf -d' read, so the headers will be on the first line of the file
137
+    local headers
138
+    headers=( $( head -n 1 "${datafile}" | sed 's/^# //g' | tr ';' ' ' ) )
139
+
140
+    local records
141
+    local width
142
+    records="$(( $( cat "${datafile}" | wc -l ) - 1 ))" # one of these lines will be the header comment
143
+    if [[ "${records}" -gt 90 ]]; then
144
+        width="$(echo "8.5 + ${records}*0.025" | bc )"
145
+    else 
146
+        width="8.5"
147
+    fi
148
+
149
+    local gnuplot_directive=( "set terminal pdf size ${width}in,$(( 2 * (${#headers[@]} - 1) ))in" \
150
+                              "set output \"${LOG_DIR}/${plotname}.pdf\"" \
151
+                              "set datafile separator \";\"" \    
152
+                              "set xdata time" \
153
+                              "set timefmt '%Y-%m-%d %H:%M:%S UTC'" \
154
+                              "set tmargin 1" \
155
+                              "set bmargin 1" \
156
+                              "set lmargin 20" \
157
+                              "set rmargin 20" \
158
+                              "set multiplot layout ${#headers[@]},1 title \"\n${plotname}\n\"" \
159
+                              "unset title" )
160
+
161
+    local i
162
+    for (( i = 1; i < "${#headers[@]}"; i++ )); do
163
+        local header
164
+        header="${headers[$i]}"
165
+
166
+        if (( $i == ${#headers[@]} - 1 )); then
167
+            # we need x-tick labels on the bottom plot
168
+            gnuplot_directive+=( "set xtics format '%H:%M:%S' rotate by -90" )
169
+        else 
170
+            gnuplot_directive+=( "set format x ''" )
171
+        fi 
172
+
173
+        gnuplot_directive+=( "plot \"${datafile}\" using 1:$(( $i + 1 )) title \"${header}\" with lines" )
174
+    done
175
+
176
+    # concatenate the array with newlines to get the final directive to send to gnuplot
177
+    gnuplot_directive="$( IFS=$'\n'; echo "${gnuplot_directive[*]}" )"
178
+
179
+    echo -e "\$ gnuplot <<< ${gnuplot_directive}\n" >> "${LOG_DIR}/gnuplot.log"
180
+    gnuplot <<< "${gnuplot_directive}" >> "${LOG_DIR}/gnuplot.log" 2>&1
181
+    echo -e "\n\n" >> "${LOG_DIR}/gnuplot.log"
182
+
183
+    echo "[INFO] Stacked plot for log subset \"${plotname}\" written to ${LOG_DIR}/${plotname}.pdf"
184
+}
185
+
186
+
187
+# os::log::start_system_logger installs the system logger and begins logging
188
+#
189
+# Globals:
190
+#  - LOG_DIR
191
+# Arguments:
192
+#  None
193
+# Returns:
194
+#  - export LOGGER_PID
195
+#  - export SAR_LOGFILE
196
+function os::log::start_system_logger() {
197
+    if ! which sar >/dev/null 2>&1; then
198
+        echo "[WARNING] System logger could not be started, 'sar' binary not found in this environment."
199
+        return 0
200
+    fi
201
+
202
+    readonly SAR_LOGFILE="${LOG_DIR}/sar.log"
203
+    export SAR_LOGFILE
204
+
205
+    ( os::log::internal::run_system_logger "${SAR_LOGFILE}" "${LOG_DIR}/sar_stderr.log" ) &
206
+    LOGGER_PID=$!
207
+    readonly LOGGER_PID
208
+    export LOGGER_PID
209
+
210
+    os::log::install_system_logger_cleanup
211
+}
212
+
213
+
214
+# os::log::internal::run_system_logger runs the system logger. This function should be run in the background.
215
+# 'sar' is configured to run once a second for 24 hours, so the cleanup trap should be installed to ensure that
216
+# the process is killed once the parent script is finished.
217
+#
218
+# Globals:
219
+#  None
220
+# Arguments:
221
+#  - 1: file to log binary outut to
222
+#  - 2: file to log stderr of the logger to
223
+# Returns:
224
+#  None
225
+function os::log::internal::run_system_logger() {
226
+    local binary_logfile=$1
227
+    local stderr_logfile=$2
228
+
229
+    sar -A -o "${binary_logfile}" 1 86400 1>/dev/null 2>"${stderr_logfile}"
230
+}
0 231
\ No newline at end of file
... ...
@@ -11,6 +11,7 @@ STARTTIME=$(date +%s)
11 11
 OS_ROOT=$(dirname "${BASH_SOURCE}")/..
12 12
 cd "${OS_ROOT}"
13 13
 source "${OS_ROOT}/hack/util.sh"
14
+source "${OS_ROOT}/hack/lib/log.sh"
14 15
 os::log::install_errexit
15 16
 
16 17
 function cleanup()
... ...
@@ -72,6 +73,8 @@ reset_tmp_dir
72 72
 
73 73
 echo "Logging to ${LOG_DIR}..."
74 74
 
75
+os::log::start_system_logger
76
+
75 77
 # Prevent user environment from colliding with the test setup
76 78
 unset KUBECONFIG
77 79
 
... ...
@@ -10,6 +10,7 @@ set -o pipefail
10 10
 STARTTIME=$(date +%s)
11 11
 OS_ROOT=$(dirname "${BASH_SOURCE}")/..
12 12
 source "${OS_ROOT}/hack/util.sh"
13
+source "${OS_ROOT}/hack/lib/log.sh"
13 14
 
14 15
 echo "[INFO] Starting containerized end-to-end test"
15 16
 
... ...
@@ -67,6 +68,8 @@ function cleanup()
67 67
 trap "exit" INT TERM
68 68
 trap "cleanup" EXIT
69 69
 
70
+os::log::start_system_logger
71
+
70 72
 out=$(
71 73
 	set +e
72 74
 	docker stop origin 2>&1
... ...
@@ -10,6 +10,7 @@ set -o pipefail
10 10
 STARTTIME=$(date +%s)
11 11
 OS_ROOT=$(dirname "${BASH_SOURCE}")/..
12 12
 source "${OS_ROOT}/hack/util.sh"
13
+source "${OS_ROOT}/hack/lib/log.sh"
13 14
 os::log::install_errexit
14 15
 
15 16
 ensure_iptables_or_die
... ...
@@ -42,6 +43,9 @@ TMPDIR="${TMPDIR:-"/tmp"}"
42 42
 BASETMPDIR="${BASETMPDIR:-${TMPDIR}/openshift-e2e}"
43 43
 setup_env_vars
44 44
 reset_tmp_dir
45
+
46
+os::log::start_system_logger
47
+
45 48
 configure_os_server
46 49
 start_os_server
47 50
 
... ...
@@ -9,6 +9,7 @@ OS_ROOT=$(dirname "${BASH_SOURCE}")/..
9 9
 source "${OS_ROOT}/hack/common.sh"
10 10
 source "${OS_ROOT}/hack/util.sh"
11 11
 source "${OS_ROOT}/hack/text.sh"
12
+source "${OS_ROOT}/hack/lib/log.sh"
12 13
 os::log::install_errexit
13 14
 
14 15
 # Go to the top of the tree.
... ...
@@ -20,10 +21,7 @@ export ETCD_HOST=${ETCD_HOST:-127.0.0.1}
20 20
 export ETCD_PORT=${ETCD_PORT:-44001}
21 21
 export ETCD_PEER_PORT=${ETCD_PEER_PORT:-47001}
22 22
 
23
-
24
-set +e
25
-
26
-if [ "$(which etcd 2>/dev/null)" == "" ]; then
23
+if ! which etcd >/dev/null 2>&1; then
27 24
 	if [[ ! -f ${OS_ROOT}/_tools/etcd/bin/etcd ]]; then
28 25
 		echo "etcd must be in your PATH or installed in _tools/etcd/bin/ with hack/install-etcd.sh"
29 26
 		exit 1
... ...
@@ -31,11 +29,6 @@ if [ "$(which etcd 2>/dev/null)" == "" ]; then
31 31
 	export PATH="${OS_ROOT}/_tools/etcd/bin:$PATH"
32 32
 fi
33 33
 
34
-# Stop on any failures
35
-set -e
36
-
37
-
38
-
39 34
 function cleanup() {
40 35
 	out=$?
41 36
 	set +e
... ...
@@ -48,7 +41,7 @@ function cleanup() {
48 48
 	exit $out
49 49
 }
50 50
 
51
-
51
+trap cleanup EXIT SIGINT
52 52
 
53 53
 package="${OS_TEST_PACKAGE:-test/integration}"
54 54
 tags="${OS_TEST_TAGS:-integration !docker etcd}"
... ...
@@ -59,7 +52,6 @@ export BASETMPDIR="${BASETMPDIR:-${TMPDIR}/openshift-integration}"
59 59
 rm -rf ${BASETMPDIR} | true
60 60
 mkdir -p ${BASETMPDIR}
61 61
 
62
-
63 62
 echo
64 63
 echo "Test ${package} -tags='${tags}' ..."
65 64
 echo
... ...
@@ -78,7 +70,10 @@ pushd "${testdir}" &>/dev/null
78 78
 echo "Building test executable..."
79 79
 CGO_ENABLED=0 go test -c -tags="${tags}" "${OS_GO_PACKAGE}/${package}"
80 80
 popd &>/dev/null
81
-
81
+	
82
+LOG_DIR="${BASETMPDIR}/logs"
83
+mkdir -p "${LOG_DIR}" 
84
+os::log::start_system_logger
82 85
 
83 86
 # Start etcd
84 87
 echo "Starting etcd..."
... ...
@@ -95,8 +90,6 @@ wait_for_url "http://${ETCD_HOST}:${ETCD_PORT}/version" "etcd: " 0.25 160
95 95
 curl -X PUT	"http://${ETCD_HOST}:${ETCD_PORT}/v2/keys/_test"
96 96
 echo
97 97
 
98
-trap cleanup EXIT SIGINT
99
-
100 98
 function exectest() {
101 99
 	echo "Running $1..."
102 100
 
... ...
@@ -13,6 +13,7 @@ source "${OS_ROOT}/hack/util.sh"
13 13
 source "${OS_ROOT}/hack/cmd_util.sh"
14 14
 os::log::install_errexit
15 15
 
16
+
16 17
 for tool in ${OS_ROOT}/tools/*; do
17 18
 	test_file=${tool}/test/integration.sh
18 19
 	if [ -e ${test_file} ]; then
... ...
@@ -12,6 +12,7 @@ set -o pipefail
12 12
 OS_ROOT=$(dirname "${BASH_SOURCE}")/../..
13 13
 source "${OS_ROOT}/hack/util.sh"
14 14
 source "${OS_ROOT}/hack/common.sh"
15
+source "${OS_ROOT}/hack/lib/log.sh"
15 16
 os::log::install_errexit
16 17
 cd "${OS_ROOT}"
17 18
 
... ...
@@ -39,6 +40,9 @@ echo "[INFO] Starting server"
39 39
 
40 40
 setup_env_vars
41 41
 reset_tmp_dir
42
+
43
+os::log::start_system_logger
44
+
42 45
 configure_os_server
43 46
 start_os_server
44 47
 
... ...
@@ -15,6 +15,7 @@ export KUBE_REPO_ROOT="${GOPATH}/src/k8s.io/kubernetes"
15 15
 OS_ROOT=$(dirname "${BASH_SOURCE}")/../..
16 16
 source "${OS_ROOT}/hack/util.sh"
17 17
 source "${OS_ROOT}/hack/common.sh"
18
+source "${OS_ROOT}/hack/lib/log.sh"
18 19
 os::log::install_errexit
19 20
 cd "${OS_ROOT}"
20 21
 
... ...
@@ -106,6 +107,9 @@ if [[ -z ${TEST_ONLY+x} ]]; then
106 106
 
107 107
   setup_env_vars
108 108
   reset_tmp_dir
109
+
110
+  os::log::start_system_logger
111
+
109 112
   # when selinux is enforcing, the volume dir selinux label needs to be
110 113
   # svirt_sandbox_file_t
111 114
   #
... ...
@@ -11,6 +11,7 @@ set -o pipefail
11 11
 OS_ROOT=$(dirname "${BASH_SOURCE}")/../..
12 12
 source "${OS_ROOT}/hack/util.sh"
13 13
 source "${OS_ROOT}/hack/common.sh"
14
+source "${OS_ROOT}/hack/lib/log.sh"
14 15
 os::log::install_errexit
15 16
 cd "${OS_ROOT}"
16 17
 
... ...
@@ -40,6 +41,9 @@ echo "[INFO] Starting server"
40 40
 ensure_iptables_or_die
41 41
 setup_env_vars
42 42
 reset_tmp_dir
43
+
44
+os::log::start_system_logger
45
+
43 46
 configure_os_server
44 47
 start_os_server
45 48
 
... ...
@@ -18,6 +18,7 @@ export SHELLOPTS
18 18
 OS_ROOT=$(dirname "${BASH_SOURCE}")/../..
19 19
 source "${OS_ROOT}/hack/util.sh"
20 20
 source "${OS_ROOT}/hack/common.sh"
21
+source "${OS_ROOT}/hack/lib/log.sh"
21 22
 os::log::install_errexit
22 23
 
23 24
 # These strings filter the available tests.
... ...
@@ -226,6 +227,8 @@ else
226 226
   setup_env_vars
227 227
   reset_tmp_dir
228 228
 
229
+  os::log::start_system_logger
230
+
229 231
   os::log::info "Building docker-in-docker images"
230 232
   ${CLUSTER_CMD} build-images
231 233