gitlab.com/gitlab-org/labkit@v1.21.0/log/initialization_test.go (about)

     1  package log
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"regexp"
     8  	"testing"
     9  	"time"
    10  
    11  	logrus "github.com/sirupsen/logrus"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestInitialize(t *testing.T) {
    16  	rfc3339baseTimestampPattern := "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}"
    17  	iso8601baseTimestampPattern := "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}"
    18  
    19  	tests := []struct {
    20  		name         string
    21  		opts         []LoggerOption
    22  		envVars      map[string]string
    23  		wantErr      bool
    24  		regexps      []*regexp.Regexp
    25  		infoMessage  string
    26  		debugMessage string
    27  	}{
    28  		{
    29  			name: "trivial",
    30  			regexps: []*regexp.Regexp{
    31  				regexp.MustCompile(`^$`),
    32  			},
    33  		},
    34  		{
    35  			name:        "simple",
    36  			infoMessage: "Hello",
    37  			regexps: []*regexp.Regexp{
    38  				regexp.MustCompile(`^time=.*level=info.*msg=Hello`),
    39  			},
    40  		},
    41  		{
    42  			name: "simple-json",
    43  			opts: []LoggerOption{
    44  				WithFormatter("json"),
    45  			},
    46  			infoMessage: "Hello",
    47  			regexps: []*regexp.Regexp{
    48  				regexp.MustCompile(`^{"level":"info","msg":"Hello"`),
    49  			},
    50  		},
    51  		{
    52  			name: "simple-text",
    53  			opts: []LoggerOption{
    54  				WithFormatter("text"),
    55  			},
    56  			infoMessage: "Hello",
    57  			regexps: []*regexp.Regexp{
    58  				regexp.MustCompile(`^time=.*level=info.*msg=Hello`),
    59  			},
    60  		},
    61  		{
    62  			name: "invalid-formatter",
    63  			opts: []LoggerOption{
    64  				WithFormatter("rubbish"),
    65  			},
    66  			infoMessage: "Hello",
    67  			regexps: []*regexp.Regexp{
    68  				regexp.MustCompile(`^time=.*level=warning.*msg="unknown logging format rubbish, ignoring option"`),
    69  				regexp.MustCompile(`(?m)^time=.*level=info.*msg=Hello`),
    70  			},
    71  		},
    72  		{
    73  			name: "loglevel-below-threshold",
    74  			opts: []LoggerOption{
    75  				WithLogLevel("info"),
    76  			},
    77  			debugMessage: "Hello",
    78  			regexps: []*regexp.Regexp{
    79  				regexp.MustCompile(`^$`),
    80  			},
    81  		},
    82  		{
    83  			name: "loglevel-at-threshold",
    84  			opts: []LoggerOption{
    85  				WithLogLevel("info"),
    86  			},
    87  			infoMessage:  "InfoHello",
    88  			debugMessage: "DebugHello",
    89  			regexps: []*regexp.Regexp{
    90  				regexp.MustCompile(`^time=.*level=info.*msg=InfoHello\n$`),
    91  			},
    92  		},
    93  		{
    94  			name: "invalid-threshold",
    95  			opts: []LoggerOption{
    96  				WithLogLevel("garbage"),
    97  			},
    98  			infoMessage:  "InfoHello",
    99  			debugMessage: "DebugHello",
   100  
   101  			regexps: []*regexp.Regexp{
   102  				regexp.MustCompile(`level=warning msg="unknown log level, ignoring option`),
   103  				regexp.MustCompile(`(?m)^time=.*level=info.*msg=InfoHello\n$`),
   104  			},
   105  		},
   106  		{
   107  			name: "invalid-threshold-json-format",
   108  			opts: []LoggerOption{
   109  				WithFormatter("json"),
   110  				WithLogLevel("garbage"),
   111  			},
   112  			infoMessage:  "InfoHello",
   113  			debugMessage: "DebugHello",
   114  
   115  			regexps: []*regexp.Regexp{
   116  				regexp.MustCompile(`"msg":"unknown log level, ignoring option: not a valid logrus Level`),
   117  				regexp.MustCompile(`"msg":"InfoHello"`),
   118  			},
   119  		},
   120  		{
   121  			name: "emit-to-stderr",
   122  			opts: []LoggerOption{
   123  				WithOutputName("stderr"),
   124  			},
   125  			infoMessage: "InfoHello",
   126  			regexps: []*regexp.Regexp{
   127  				regexp.MustCompile(`^time=.*level=info.*msg=InfoHello\n$`),
   128  			},
   129  		},
   130  		{
   131  			name: "emit-to-stdout",
   132  			opts: []LoggerOption{
   133  				WithOutputName("stdout"),
   134  			},
   135  			infoMessage: "InfoHello",
   136  			regexps: []*regexp.Regexp{
   137  				regexp.MustCompile(`^time=.*level=info.*msg=InfoHello\n$`),
   138  			},
   139  		},
   140  		{
   141  			name: "emit-to-file",
   142  			opts: []LoggerOption{
   143  				WithOutputName("/tmp/test.log"),
   144  			},
   145  			infoMessage: "InfoHello",
   146  			regexps: []*regexp.Regexp{
   147  				regexp.MustCompile(`^time=.*level=info.*msg=InfoHello\n$`),
   148  			},
   149  		},
   150  		{
   151  			name: "color",
   152  			opts: []LoggerOption{
   153  				WithFormatter("color"),
   154  			},
   155  			infoMessage: "It's starting to look like a triple rainbow.",
   156  			regexps: []*regexp.Regexp{
   157  				regexp.MustCompile("^\x1b\\[36mINFO\x1b\\[0m\\[0000\\] It's starting to look like a triple rainbow."),
   158  			},
   159  		},
   160  		{
   161  			name: "timezone-utc-plus-12",
   162  			opts: []LoggerOption{
   163  				WithTimezone(time.FixedZone("UTC+12", 12*60*60)),
   164  			},
   165  			infoMessage: "test",
   166  			regexps: []*regexp.Regexp{
   167  				regexp.MustCompile(`^time=[^ ]+\+12:00.*level=info.*msg=test\n$`),
   168  			},
   169  		},
   170  		{
   171  			name: "timezone-utc-plus-zero",
   172  			opts: []LoggerOption{
   173  				WithTimezone(time.UTC),
   174  			},
   175  			infoMessage: "test",
   176  			regexps: []*regexp.Regexp{
   177  				regexp.MustCompile(`^time=[^ ]+Z.*level=info.*msg=test\n$`),
   178  			},
   179  		},
   180  		{
   181  			name: "two-formatters",
   182  			opts: []LoggerOption{
   183  				WithTimezone(time.FixedZone("UTC-12", -12*60*60)),
   184  				WithFormatter("json"),
   185  			},
   186  			infoMessage: "test",
   187  			regexps: []*regexp.Regexp{
   188  				regexp.MustCompile(`"time":"[^"]+-12:00"`),
   189  				regexp.MustCompile(`"msg":"test"`),
   190  			},
   191  		},
   192  		{
   193  			name: "timestamp-format-default-formatter-and-default-layout",
   194  			opts: []LoggerOption{
   195  				WithTimezone(time.UTC), // using a fixed timezone for portable/reproducible tests
   196  			},
   197  			infoMessage: "Hello",
   198  			regexps: []*regexp.Regexp{
   199  				regexp.MustCompile(fmt.Sprintf("^time=\"%sZ\"", rfc3339baseTimestampPattern)),
   200  			},
   201  		},
   202  		{
   203  			name: "timestamp-format-default-formatter-and-iso8601-layout",
   204  			opts: []LoggerOption{
   205  				WithTimezone(time.UTC),
   206  			},
   207  			envVars:     map[string]string{iso8601TimestampEnvKey: ""},
   208  			infoMessage: "Hello",
   209  			regexps: []*regexp.Regexp{
   210  				regexp.MustCompile(fmt.Sprintf("^time=\"%sZ\"", iso8601baseTimestampPattern)),
   211  			},
   212  		},
   213  		{
   214  			name: "timestamp-format-json-formatter-and-default-layout",
   215  			opts: []LoggerOption{
   216  				WithFormatter("json"),
   217  				WithTimezone(time.UTC),
   218  			},
   219  			infoMessage: "Hello",
   220  			regexps: []*regexp.Regexp{
   221  				regexp.MustCompile(fmt.Sprintf("\"time\":\"%sZ\"", rfc3339baseTimestampPattern)),
   222  			},
   223  		},
   224  		{
   225  			name: "timestamp-format-json-formatter-and-iso8601-layout",
   226  			opts: []LoggerOption{
   227  				WithFormatter("json"),
   228  				WithTimezone(time.UTC),
   229  			},
   230  			envVars:     map[string]string{iso8601TimestampEnvKey: ""},
   231  			infoMessage: "Hello",
   232  			regexps: []*regexp.Regexp{
   233  				regexp.MustCompile(fmt.Sprintf("\"time\":\"%sZ\"", iso8601baseTimestampPattern)),
   234  			},
   235  		},
   236  		{
   237  			name: "timestamp-format-text-formatter-and-default-layout",
   238  			opts: []LoggerOption{
   239  				WithFormatter("text"),
   240  				WithTimezone(time.UTC),
   241  			},
   242  			infoMessage: "Hello",
   243  			regexps: []*regexp.Regexp{
   244  				regexp.MustCompile(fmt.Sprintf("^time=\"%sZ\"", rfc3339baseTimestampPattern)),
   245  			},
   246  		},
   247  		{
   248  			name: "timestamp-format-text-formatter-and-iso8601-layout",
   249  			opts: []LoggerOption{
   250  				WithFormatter("text"),
   251  				WithTimezone(time.UTC),
   252  			},
   253  			envVars:     map[string]string{iso8601TimestampEnvKey: ""},
   254  			infoMessage: "Hello",
   255  			regexps: []*regexp.Regexp{
   256  				regexp.MustCompile(fmt.Sprintf("^time=\"%sZ\"", iso8601baseTimestampPattern)),
   257  			},
   258  		},
   259  		{
   260  			name: "timestamp-format-iso8601-layout-and-timezone-offset",
   261  			opts: []LoggerOption{
   262  				WithTimezone(time.FixedZone("UTC+12", 12*60*60)),
   263  			},
   264  			envVars:     map[string]string{iso8601TimestampEnvKey: ""},
   265  			infoMessage: "Hello",
   266  			regexps: []*regexp.Regexp{
   267  				regexp.MustCompile(fmt.Sprintf("^time=\"%s\\+12:00\"", iso8601baseTimestampPattern)),
   268  			},
   269  		},
   270  	}
   271  
   272  	for _, tt := range tests {
   273  		t.Run(tt.name, func(t *testing.T) {
   274  			var buf bytes.Buffer
   275  
   276  			opts := append([]LoggerOption{}, tt.opts...)
   277  			opts = append(opts, WithWriter(&buf))
   278  
   279  			backupAndSetEnvVars(t, tt.envVars)
   280  
   281  			closer, err := Initialize(opts...)
   282  			defer closer.Close()
   283  
   284  			if tt.infoMessage != "" {
   285  				logrus.Info(tt.infoMessage)
   286  			}
   287  
   288  			if tt.debugMessage != "" {
   289  				logrus.Debug(tt.debugMessage)
   290  			}
   291  
   292  			if tt.wantErr {
   293  				require.Error(t, err)
   294  			} else {
   295  				require.NoError(t, err)
   296  			}
   297  
   298  			bytes := buf.Bytes()
   299  			for _, re := range tt.regexps {
   300  				require.Regexp(t, re, string(bytes))
   301  			}
   302  		})
   303  	}
   304  }
   305  
   306  func backupAndSetEnvVars(t *testing.T, vars map[string]string) {
   307  	t.Helper()
   308  
   309  	// backup any env vars and override them for the duration of t
   310  	bkpSetEnvVars := make(map[string]string)
   311  	var bkpUnsetEnvVars []string
   312  	for k := range vars {
   313  		v, exists := os.LookupEnv(k)
   314  		if exists {
   315  			bkpSetEnvVars[k] = v
   316  		} else {
   317  			bkpUnsetEnvVars = append(bkpUnsetEnvVars, k)
   318  		}
   319  		require.NoError(t, os.Setenv(k, v))
   320  	}
   321  
   322  	// restore any env vars at the end of t
   323  	t.Cleanup(func() {
   324  		for k, v := range bkpSetEnvVars {
   325  			require.NoError(t, os.Setenv(k, v))
   326  		}
   327  		for _, k := range bkpUnsetEnvVars {
   328  			require.NoError(t, os.Unsetenv(k))
   329  		}
   330  	})
   331  }