Skip to content

Commit c7a7704

Browse files
mackinleysmithStephen Gutekanst
authored and
Stephen Gutekanst
committed
Include the contents of logs when postgres fails to start or initdb fails (fergusstrange#84)
* fix: print log contents when postgres fails to start * fix: flush the logger before attempting to read * test: failure is included in error when postgres fails to start * fix: include log contents with initialization errors * fix: flush the logger before reading * refactor: read by file name * test: logs are output on initialization failure * fix: os -> ioutil for older versions of Go * fix: os -> ioutil for older versions of Go * lint: stop cuddling 😆 * fix: bail if we can't read the logs after 10s * refactor: move log reading into function * refactor: use timeout version for startPostgres too * test: logging method * test: error case * test: logging method content * fix: imports * feat: include log reading error in log output
1 parent 92d54a9 commit c7a7704

File tree

6 files changed

+76
-4
lines changed

6 files changed

+76
-4
lines changed

embedded_postgres.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,10 @@ func startPostgres(ep *EmbeddedPostgres) error {
219219
}
220220

221221
if err := postgresProcess.Start(); err != nil {
222-
return fmt.Errorf("could not start postgres using %s: %w", postgresProcess.String(), err)
222+
_ = ep.syncedLogger.flush()
223+
logContent, _ := readLogsOrTimeout(ep.syncedLogger.file)
224+
225+
return fmt.Errorf("could not start postgres using %s:\n%s", postgresProcess.String(), string(logContent))
223226
}
224227

225228
waitErrC := make(chan error, 1)

embedded_postgres_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,13 @@ func Test_ErrorWhenCannotStartPostgresProcess(t *testing.T) {
226226
}
227227

228228
database.initDatabase = func(binaryExtractLocation, runtimePath, dataLocation, username, password, locale string, useUnixSocket string, logger *os.File) error {
229+
_, _ = logger.Write([]byte("ah it did not work"))
229230
return nil
230231
}
231232

232233
err = database.Start()
233234

234-
assert.Contains(t, err.Error(), fmt.Sprintf(`could not start postgres using %s/bin/postgres -D %s/data -p 5432`, extractPath, extractPath))
235+
assert.EqualError(t, err.Error(), fmt.Sprintf("could not start postgres using %s/bin/pg_ctl start -w -D %s/data -o \"-p 5432\":\nah it did not work", extractPath, extractPath))
235236
}
236237

237238
func Test_CustomConfig(t *testing.T) {

logging.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package embeddedpostgres
33
import (
44
"fmt"
55
"io"
6+
"io/ioutil"
67
"os"
8+
"time"
79
)
810

911
type syncedLogger struct {
@@ -53,3 +55,27 @@ func (s *syncedLogger) flush() error {
5355

5456
return nil
5557
}
58+
59+
func readLogsOrTimeout(logger *os.File) (logContent []byte, err error) {
60+
logContent = []byte("logs could not be read")
61+
62+
logContentChan := make(chan []byte, 1)
63+
errChan := make(chan error, 1)
64+
65+
go func() {
66+
if actualLogContent, err := ioutil.ReadFile(logger.Name()); err == nil {
67+
logContentChan <- actualLogContent
68+
} else {
69+
errChan <- err
70+
}
71+
}()
72+
73+
select {
74+
case logContent = <-logContentChan:
75+
case err = <-errChan:
76+
case <-time.After(10 * time.Second):
77+
err = fmt.Errorf("timed out waiting for logs")
78+
}
79+
80+
return logContent, err
81+
}

logging_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package embeddedpostgres
22

33
import (
4+
"fmt"
5+
"io/ioutil"
46
"os"
57
"testing"
68

79
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
811
)
912

1013
type customLogger struct {
@@ -56,3 +59,25 @@ func Test_SyncedLogger_NoErrorDuringFlush(t *testing.T) {
5659

5760
assert.Equal(t, "some logs\non a new line", string(logger.logLines))
5861
}
62+
63+
func Test_readLogsOrTimeout(t *testing.T) {
64+
logFile, err := ioutil.TempFile("", "prepare_database_test_log")
65+
if err != nil {
66+
panic(err)
67+
}
68+
69+
logContent, err := readLogsOrTimeout(logFile)
70+
assert.NoError(t, err)
71+
assert.Equal(t, []byte(""), logContent)
72+
73+
_, _ = logFile.Write([]byte("and here are the logs!"))
74+
75+
logContent, err = readLogsOrTimeout(logFile)
76+
assert.NoError(t, err)
77+
assert.Equal(t, []byte("and here are the logs!"), logContent)
78+
79+
require.NoError(t, os.Remove(logFile.Name()))
80+
logContent, err = readLogsOrTimeout(logFile)
81+
assert.Equal(t, []byte("logs could not be read"), logContent)
82+
assert.EqualError(t, err, fmt.Sprintf("open %s: no such file or directory", logFile.Name()))
83+
}

prepare_database.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ func defaultInitDatabase(binaryExtractLocation, runtimePath, pgDataDir, username
4646
postgresInitDBProcess.Stdout = logger
4747

4848
if err = postgresInitDBProcess.Run(); err != nil {
49-
return fmt.Errorf("unable to init database using '%s': %w", postgresInitDBProcess.String(), err)
49+
logContent, readLogsErr := readLogsOrTimeout(logger) // we want to preserve the original error
50+
if readLogsErr != nil {
51+
logContent = []byte(string(logContent) + " - " + readLogsErr.Error())
52+
}
53+
return fmt.Errorf("unable to init database using '%s': %w\n%s", postgresInitDBProcess.String(), err, string(logContent))
5054
}
5155

5256
if err = os.Remove(passwordFile); err != nil {

prepare_database_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package embeddedpostgres
33
import (
44
"errors"
55
"fmt"
6+
"io/ioutil"
67
"os"
78
"path/filepath"
89
"testing"
@@ -27,6 +28,11 @@ func Test_defaultInitDatabase_ErrorWhenCannotStartInitDBProcess(t *testing.T) {
2728
panic(err)
2829
}
2930

31+
logFile, err := ioutil.TempFile("", "prepare_database_test_log")
32+
if err != nil {
33+
panic(err)
34+
}
35+
3036
defer func() {
3137
if err := os.RemoveAll(binTempDir); err != nil {
3238
panic(err)
@@ -35,15 +41,22 @@ func Test_defaultInitDatabase_ErrorWhenCannotStartInitDBProcess(t *testing.T) {
3541
if err := os.RemoveAll(runtimeTempDir); err != nil {
3642
panic(err)
3743
}
44+
45+
if err := os.Remove(logFile.Name()); err != nil {
46+
panic(err)
47+
}
3848
}()
3949

40-
err = defaultInitDatabase(binTempDir, runtimeTempDir, filepath.Join(runtimeTempDir, "data"), "Tom", "Beer", "", "", os.Stderr)
50+
_, _ = logFile.Write([]byte("and here are the logs!"))
51+
52+
err = defaultInitDatabase(binTempDir, runtimeTempDir, filepath.Join(runtimeTempDir, "data"), "Tom", "Beer", "", "", logFile)
4153

4254
assert.NotNil(t, err)
4355
assert.Contains(t, err.Error(), fmt.Sprintf("unable to init database using '%s/bin/initdb -A password --pwfile=%s/pwfile -U Tom -D %s/data'",
4456
binTempDir,
4557
runtimeTempDir,
4658
runtimeTempDir))
59+
assert.Contains(t, err.Error(), "and here are the logs!")
4760
assert.FileExists(t, filepath.Join(runtimeTempDir, "pwfile"))
4861
}
4962

0 commit comments

Comments
 (0)