Skip to content

Commit 965782d

Browse files
committed
log: Add stack trace to FATAL output
Also makes use of the `skip_callers` argument to `@go.print_stack_trace` to ensure that the functions in this module will not appear in the stack trace themselves whenever they invoke `@go.log FATAL`.
1 parent a0f4413 commit 965782d

File tree

5 files changed

+74
-21
lines changed

5 files changed

+74
-21
lines changed

lib/log

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ declare __GO_LOG_LEVELS_FORMATTED=()
9696
# Set by @go.critical_section_{begin,end}
9797
declare __GO_CRITICAL_SECTION=0
9898

99+
# DO NOT EDIT: Determines number of stack trace levels to skip for FATAL logs.
100+
declare __GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS=1
101+
99102
# Outputs a single log line that may contain terminal control characters.
100103
#
101104
# Usage:
@@ -171,7 +174,8 @@ declare __GO_CRITICAL_SECTION=0
171174

172175
printf "$formatted_log_level ${args[*]}$close_code\n" >&"$level_fd"
173176

174-
if [[ "$log_level" == FATAL ]]; then
177+
if [[ "$log_level" == 'FATAL' ]]; then
178+
@go.print_stack_trace "$__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS" >&2
175179
exit "$exit_status"
176180
fi
177181
return "$exit_status"
@@ -190,18 +194,21 @@ declare __GO_CRITICAL_SECTION=0
190194
local log_level="$1"
191195
local format_code="$2"
192196
local level_fd="${3:-1}"
197+
((++__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS))
193198

194199
if [[ -n "$__GO_LOG_INIT" ]]; then
195-
@go.log 'FATAL' "Can't set logging level $log_level; already initialized"
200+
@go.log FATAL "Can't set logging level $log_level; already initialized"
196201
elif [[ "$level_fd" != 'keep' && ! "$level_fd" =~ ^[1-9]+[0-9]*$ ]]; then
197202
@go.log FATAL "File descriptor $level_fd for $log_level must be > 0"
198203
elif ! echo -n 2>/dev/null >&"$level_fd"; then
199204
@go.log FATAL "File descriptor $level_fd for $log_level isn't open"
200205
fi
206+
((--__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS))
201207

202208
local __go_log_level_index=0
203209
if ! _@go.log_level_index "$log_level"; then
204210
if [[ "$format_code" == 'keep' || "$level_fd" == 'keep' ]]; then
211+
((++__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS))
205212
@go.log FATAL "Can't keep defaults for nonexistent log level $log_level"
206213
fi
207214
_GO_LOG_LEVELS+=("$log_level")
@@ -270,6 +277,7 @@ declare __GO_CRITICAL_SECTION=0
270277

271278
if [[ "$exit_status" -ne '0' ]]; then
272279
if [[ "$__GO_CRITICAL_SECTION" -ne '0' ]]; then
280+
((++__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS))
273281
@go.log FATAL "$exit_status" "$cmd_string"
274282
fi
275283
@go.log ERROR "$exit_status" "$cmd_string"
@@ -312,17 +320,20 @@ declare __GO_CRITICAL_SECTION=0
312320

313321
@go.log START Project setup in "$_GO_ROOTDIR"
314322

323+
((++__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS))
315324
if [[ ! -f "$setup_script" ]]; then
316325
@go.log FATAL "Create $setup_script before invoking $FUNCNAME."
317326
elif [[ ! -x "$setup_script" ]]; then
318327
@go.log FATAL "$setup_script is not executable."
319328
fi
329+
((--__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS))
320330

321331
@go.log RUN "${setup_script#$_GO_ROOTDIR/}" "$@"
322332
_@go.run_command_script "$setup_script" "$@"
323333
setup_status="$?"
324334

325335
if [[ "$setup_status" -ne '0' ]]; then
336+
((++__GO_LOG_FATAL_STACK_TRACE_SKIP_CALLERS))
326337
@go.log FATAL "$setup_status" "Project setup failed"
327338
fi
328339

tests/log/add-update.bats

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,38 @@ teardown() {
1515
'@go.add_or_update_log_level INFO keep keep'
1616
assert_failure
1717
assert_log_equals INFO 'Hello, World!' \
18-
FATAL "Can't set logging level INFO; already initialized"
18+
FATAL "Can't set logging level INFO; already initialized" \
19+
" $TEST_GO_SCRIPT:5 main"
1920
}
2021

2122
@test "$SUITE: fail if file descriptor isn't a positive integer" {
2223
run_log_script '@go.add_or_update_log_level INFO keep 0'
2324
assert_failure
24-
assert_log_equals FATAL "File descriptor 0 for INFO must be > 0"
25+
assert_log_equals FATAL "File descriptor 0 for INFO must be > 0" \
26+
" $TEST_GO_SCRIPT:4 main"
2527
}
2628

2729
@test "$SUITE: fail if file descriptor isn't open" {
2830
run_log_script '@go.add_or_update_log_level INFO keep 27'
2931
assert_failure
30-
assert_log_equals FATAL "File descriptor 27 for INFO isn't open"
32+
assert_log_equals FATAL "File descriptor 27 for INFO isn't open" \
33+
" $TEST_GO_SCRIPT:4 main"
3134
}
3235

3336
@test "$SUITE: fail if keeping the format code for a nonexistent level" {
3437
run_log_script '@go.add_or_update_log_level FOOBAR keep 1'
3538
assert_failure
36-
assert_log_equals FATAL "Can't keep defaults for nonexistent log level FOOBAR"
39+
assert_log_equals \
40+
FATAL "Can't keep defaults for nonexistent log level FOOBAR" \
41+
" $TEST_GO_SCRIPT:4 main"
3742
}
3843

3944
@test "$SUITE: fail if keeping the file descriptor for a nonexistent level" {
4045
run_log_script "@go.add_or_update_log_level FOOBAR '$INFO_FORMAT' keep"
4146
assert_failure
42-
assert_log_equals FATAL "Can't keep defaults for nonexistent log level FOOBAR"
47+
assert_log_equals \
48+
FATAL "Can't keep defaults for nonexistent log level FOOBAR" \
49+
" $TEST_GO_SCRIPT:4 main"
4350
}
4451

4552
@test "$SUITE: add new log level" {

tests/log/log-command.bats

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ teardown() {
2929
'@go.critical_section_end'
3030
assert_failure
3131
assert_log_equals RUN 'failing_function foo bar baz' \
32-
FATAL 'failing_function foo bar baz (exit status 127)'
32+
FATAL 'failing_function foo bar baz (exit status 127)' \
33+
" $TEST_GO_SCRIPT:6 main"
3334
}
3435

3536
@test "$SUITE: log single failing command without executing during dry run" {
@@ -79,7 +80,8 @@ teardown() {
7980
assert_log_equals RUN 'echo Hello, World!' \
8081
'Hello, World!' \
8182
RUN "failing_function foo bar baz" \
82-
FATAL 'failing_function foo bar baz (exit status 127)'
83+
FATAL 'failing_function foo bar baz (exit status 127)' \
84+
" $TEST_GO_SCRIPT:7 main"
8385
}
8486

8587
@test "$SUITE: log multiple commands without executing during dry run" {
@@ -179,7 +181,8 @@ teardown() {
179181
RUN 'echo Hello, World!' \
180182
'Hello, World!' \
181183
RUN 'failing_function foo bar baz' \
182-
FATAL 'failing_function foo bar baz (exit status 127)'
184+
FATAL 'failing_function foo bar baz (exit status 127)' \
185+
" $TEST_GO_SCRIPT:12 main"
183186
}
184187

185188
@test "$SUITE: critical section counter does not go below zero" {
@@ -200,7 +203,8 @@ teardown() {
200203
RUN 'failing_function foo bar baz' \
201204
ERROR 'failing_function foo bar baz (exit status 127)' \
202205
RUN 'failing_function foo bar baz' \
203-
FATAL 'failing_function foo bar baz (exit status 127)'
206+
FATAL 'failing_function foo bar baz (exit status 127)' \
207+
" $TEST_GO_SCRIPT:12 main"
204208
}
205209

206210
@test "$SUITE: log and run command script using @go" {
@@ -228,9 +232,14 @@ teardown() {
228232

229233
run test-go foo bar baz
230234
assert_failure
235+
set_go_core_stack_trace_components
231236
assert_log_equals RUN 'test-go project-command-script foo bar baz' \
232237
RUN 'failing_function foo bar baz' \
233-
FATAL 'failing_function foo bar baz (exit status 127)'
238+
FATAL 'failing_function foo bar baz (exit status 127)' \
239+
" $TEST_GO_SCRIPTS_DIR/project-command-script:3 source" \
240+
"${GO_CORE_STACK_TRACE_COMPONENTS[@]}" \
241+
"$(log_command_stack_trace_item)" \
242+
" $TEST_GO_SCRIPT:5 main"
234243
}
235244

236245
@test "$SUITE: critical section in command script applies to parent script" {
@@ -246,7 +255,12 @@ teardown() {
246255

247256
run test-go foo bar baz
248257
assert_failure
258+
set_go_core_stack_trace_components
249259
assert_log_equals RUN 'test-go project-command-script foo bar baz' \
250260
RUN 'failing_function foo bar baz' \
251-
FATAL 'failing_function foo bar baz (exit status 127)'
261+
FATAL 'failing_function foo bar baz (exit status 127)' \
262+
" $TEST_GO_SCRIPTS_DIR/project-command-script:4 source" \
263+
"${GO_CORE_STACK_TRACE_COMPONENTS[@]}" \
264+
"$(log_command_stack_trace_item)" \
265+
" $TEST_GO_SCRIPT:4 main"
252266
}

tests/log/main.bats

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ teardown() {
5353
' @go.log INFO This line should be unreachable.' \
5454
'fi'
5555
assert_failure
56-
assert_log_equals FATAL 'Hello, World!'
56+
assert_log_equals FATAL 'Hello, World!' \
57+
" $TEST_GO_SCRIPT:4 main"
5758
}
5859

5960
@test "$SUITE: show status on FATAL if supplied" {
@@ -62,7 +63,8 @@ teardown() {
6263
' @go.log INFO This line should be unreachable.' \
6364
'fi'
6465
assert_failure
65-
assert_log_equals FATAL 'Hello, World! (exit status 127)'
66+
assert_log_equals FATAL 'Hello, World! (exit status 127)' \
67+
" $TEST_GO_SCRIPT:4 main"
6668
}
6769

6870
@test "$SUITE: exit with error if num format codes != num log levels" {

tests/log/setup-project.bats

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ teardown() {
1010
@test "$SUITE: fail if no 'setup' script exists" {
1111
run_log_script '@go.setup_project setup Hello, World!'
1212
assert_failure
13+
14+
local setup_script="$TEST_GO_SCRIPTS_DIR/setup"
1315
assert_log_equals START "Project setup in $TEST_GO_ROOTDIR" \
14-
FATAL "Create $TEST_GO_SCRIPTS_DIR/setup before invoking @go.setup_project."
16+
FATAL "Create $setup_script before invoking @go.setup_project." \
17+
" $TEST_GO_SCRIPT:4 main"
1518
}
1619

1720
@test "$SUITE: fail if the 'setup' script isn't executable" {
@@ -25,7 +28,8 @@ teardown() {
2528
run_log_script '@go.setup_project setup Hello, World!'
2629
assert_failure
2730
assert_log_equals START "Project setup in $TEST_GO_ROOTDIR" \
28-
FATAL "$TEST_GO_SCRIPTS_DIR/setup is not executable."
31+
FATAL "$TEST_GO_SCRIPTS_DIR/setup is not executable." \
32+
" $TEST_GO_SCRIPT:4 main"
2933
}
3034

3135
@test "$SUITE: setup project successfully using ./go script directly" {
@@ -60,23 +64,38 @@ teardown() {
6064
INFO "Run \`$TEST_GO_SCRIPT help\` to see the available commands."
6165
}
6266

63-
@test "$SUITE: setup fails" {
67+
@test "$SUITE: setup fails due to @go.log ERROR" {
6468
create_test_command_script 'setup' '@go.log ERROR 127 "$@"'
6569
run_log_script '@go.setup_project setup foo bar baz'
6670
assert_failure
71+
set_go_core_stack_trace_components
6772
assert_log_equals START "Project setup in $TEST_GO_ROOTDIR" \
6873
RUN "${TEST_GO_SCRIPTS_RELATIVE_DIR}/setup foo bar baz" \
6974
ERROR 'foo bar baz (exit status 127)' \
70-
FATAL 'Project setup failed (exit status 127)'
75+
FATAL 'Project setup failed (exit status 127)' \
76+
" $TEST_GO_SCRIPT:4 main"
7177
}
7278

73-
@test "$SUITE: Bash setup script exits directly" {
79+
@test "$SUITE: Bash setup script exits directly due to @go.log FATAL" {
7480
# Note that the "Project setup failed" message doesn't appear, because the
7581
# `setup` script was executed in the same Bash process.
7682
create_test_command_script 'setup' '@go.log FATAL 127 "$@"'
7783
run_log_script '@go.setup_project setup foo bar baz'
7884
assert_failure
85+
86+
local setup_project_stack_trace_item="$(stack_trace_item \
87+
"$_GO_CORE_DIR/lib/log" '@go.setup_project' \
88+
' _@go.run_command_script "$setup_script" "$@"')"
89+
local run_command_script_stack_trace_item
90+
91+
set_go_core_stack_trace_components
92+
run_command_script_stack_trace_item="${GO_CORE_STACK_TRACE_COMPONENTS[0]}"
93+
7994
assert_log_equals START "Project setup in $TEST_GO_ROOTDIR" \
8095
RUN "${TEST_GO_SCRIPTS_RELATIVE_DIR}/setup foo bar baz" \
81-
FATAL 'foo bar baz (exit status 127)'
96+
FATAL 'foo bar baz (exit status 127)' \
97+
" $TEST_GO_SCRIPTS_DIR/setup:2 source" \
98+
"$run_command_script_stack_trace_item" \
99+
"$setup_project_stack_trace_item" \
100+
" $TEST_GO_SCRIPT:4 main"
82101
}

0 commit comments

Comments
 (0)