Skip to content

Commit 75e0e9b

Browse files
authored
Make logging levels compliant with APM spec (#174)
* Make logging levels compliant to APM spec * Add fatal level support * Adding custom Zap-based logger * Clean up go.mod * Skip e2e tests * Add a few comments * Update docs * Map levels to native zap levels * Update doc and fix e2e error processing * Implement review comments
1 parent b59d3d7 commit 75e0e9b

File tree

12 files changed

+233
-51
lines changed

12 files changed

+233
-51
lines changed

apm-lambda-extension/e2e-testing/e2e_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import (
3333

3434
"github.com/google/uuid"
3535
"github.com/joho/godotenv"
36-
"github.com/sirupsen/logrus"
3736
"github.com/stretchr/testify/assert"
3837
)
3938

@@ -49,8 +48,8 @@ func TestEndToEnd(t *testing.T) {
4948
}
5049

5150
if os.Getenv("ELASTIC_APM_LOG_LEVEL") != "" {
52-
logLevel, _ := logrus.ParseLevel(os.Getenv("ELASTIC_APM_LOG_LEVEL"))
53-
extension.Log.Logger.SetLevel(logLevel)
51+
logLevel, _ := extension.ParseLogLevel(os.Getenv("ELASTIC_APM_LOG_LEVEL"))
52+
extension.Log.Level.SetLevel(logLevel)
5453
}
5554
if GetEnvVarValueOrSetDefault("RUN_E2E_TESTS", "false") != "true" {
5655
t.Skip("Skipping E2E tests. Please set the env. variable RUN_E2E_TESTS=true if you want to run them.")

apm-lambda-extension/e2e-testing/e2e_util.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ func RunCommandInDir(command string, args []string, dir string) {
5858
scannerOut := bufio.NewScanner(stdout)
5959
for scannerOut.Scan() {
6060
m := scannerOut.Text()
61-
extension.Log.Tracef(m)
61+
extension.Log.Debugf(m)
6262
}
6363
scannerErr := bufio.NewScanner(stderr)
6464
for scannerErr.Scan() {
6565
m := scannerErr.Text()
66-
extension.Log.Tracef(m)
66+
extension.Log.Debugf(m)
6767
}
6868
if err := e.Wait(); err != nil {
6969
extension.Log.Errorf("Could not wait for the execution of %s : %v", command, err)
@@ -81,7 +81,7 @@ func FolderExists(path string) bool {
8181
// This should only be used for showstopping errors.
8282
func ProcessError(err error) {
8383
if err != nil {
84-
extension.Log.Panic(err)
84+
extension.Log.Panic(err.Error())
8585
}
8686
}
8787

apm-lambda-extension/extension/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (e *Client) Register(ctx context.Context, filename string) (*RegisterRespon
113113
return nil, err
114114
}
115115
e.ExtensionID = httpRes.Header.Get(extensionIdentiferHeader)
116-
Log.Tracef("ExtensionID : %s", e.ExtensionID)
116+
Log.Debugf("ExtensionID : %s", e.ExtensionID)
117117
return &res, nil
118118
}
119119

apm-lambda-extension/extension/logger.go

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,59 @@
1818
package extension
1919

2020
import (
21-
"github.com/sirupsen/logrus"
22-
"go.elastic.co/ecslogrus"
21+
"fmt"
22+
"go.elastic.co/ecszap"
23+
"go.uber.org/zap"
24+
"go.uber.org/zap/zapcore"
25+
"strings"
2326
)
2427

25-
var Log *logrus.Entry
28+
type Level uint32
29+
30+
type LevelLogger struct {
31+
*zap.SugaredLogger
32+
zap.Config
33+
}
34+
35+
var Log LevelLogger
2636

2737
func init() {
28-
newLogger := logrus.New()
29-
newLogger.SetFormatter(&ecslogrus.Formatter{})
30-
newLogger.SetLevel(logrus.TraceLevel)
31-
newLoggerWithFields := newLogger.WithFields(logrus.Fields{"event.dataset": "apm-lambda-extension"})
32-
Log = newLoggerWithFields
38+
// Set ECS logging config
39+
Log.Config = zap.NewProductionConfig()
40+
Log.Config.EncoderConfig = ecszap.NewDefaultEncoderConfig().ToZapCoreEncoderConfig()
41+
// Create ECS logger
42+
logger, _ := Log.Config.Build(ecszap.WrapCoreOption(), zap.AddCaller())
43+
Log.SugaredLogger = logger.Sugar()
44+
}
45+
46+
// ParseLogLevel parses s as a logrus log level. If the level is off, the return flag is set to true.
47+
func ParseLogLevel(s string) (zapcore.Level, error) {
48+
switch strings.ToLower(s) {
49+
case "trace":
50+
return zapcore.DebugLevel, nil
51+
case "debug":
52+
return zapcore.DebugLevel, nil
53+
case "info":
54+
return zapcore.InfoLevel, nil
55+
case "warn", "warning":
56+
// "warn" exists for backwards compatibility;
57+
// "warning" is the canonical level name.
58+
return zapcore.WarnLevel, nil
59+
case "error":
60+
return zapcore.ErrorLevel, nil
61+
case "critical":
62+
return zapcore.FatalLevel, nil
63+
case "off":
64+
return zapcore.FatalLevel + 1, nil
65+
}
66+
return zapcore.InfoLevel, fmt.Errorf("invalid log level string %s", s)
67+
}
68+
69+
func SetLogOutputPaths(paths []string) {
70+
Log.Config.OutputPaths = paths
71+
logger, err := Log.Config.Build(ecszap.WrapCoreOption(), zap.AddCaller())
72+
if err != nil {
73+
Log.Errorf("Could not set log path : %v", err)
74+
}
75+
Log.SugaredLogger = logger.Sugar()
3376
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package extension
19+
20+
import (
21+
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
23+
"go.uber.org/zap/zapcore"
24+
"io/ioutil"
25+
"os"
26+
"testing"
27+
)
28+
29+
func init() {
30+
os.Unsetenv("ELASTIC_APM_LOG_LEVEL")
31+
}
32+
33+
func TestInitLogger(t *testing.T) {
34+
assert.NotNil(t, Log)
35+
}
36+
37+
func TestDefaultLogger(t *testing.T) {
38+
tempFile, err := ioutil.TempFile(t.TempDir(), "tempFileLoggerTest-")
39+
require.NoError(t, err)
40+
defer tempFile.Close()
41+
42+
SetLogOutputPaths([]string{tempFile.Name()})
43+
defer SetLogOutputPaths([]string{"stderr"})
44+
45+
Log.Infof("%s", "logger-test-info")
46+
Log.Debugf("%s", "logger-test-debug")
47+
tempFileContents, err := ioutil.ReadFile(tempFile.Name())
48+
require.NoError(t, err)
49+
assert.Regexp(t, `{"log.level":"info","@timestamp":".*","log.origin":{"file.name":"extension/logger_test.go","file.line":.*},"message":"logger-test-info","ecs.version":"1.6.0"}`, string(tempFileContents))
50+
}
51+
52+
func TestLoggerParseLogLevel(t *testing.T) {
53+
trace, _ := ParseLogLevel("TRacE")
54+
debug, _ := ParseLogLevel("dEbuG")
55+
info, _ := ParseLogLevel("InFo")
56+
warn, _ := ParseLogLevel("WaRning")
57+
errorL, _ := ParseLogLevel("eRRor")
58+
critical, _ := ParseLogLevel("CriTicaL")
59+
off, _ := ParseLogLevel("OFF")
60+
invalid, err := ParseLogLevel("Inva@Lid3")
61+
assert.Equal(t, zapcore.DebugLevel, trace)
62+
assert.Equal(t, zapcore.DebugLevel, debug)
63+
assert.Equal(t, zapcore.InfoLevel, info)
64+
assert.Equal(t, zapcore.WarnLevel, warn)
65+
assert.Equal(t, zapcore.ErrorLevel, errorL)
66+
assert.Equal(t, zapcore.FatalLevel, critical)
67+
assert.Equal(t, zapcore.FatalLevel+1, off)
68+
assert.Equal(t, zapcore.InfoLevel, invalid)
69+
assert.Error(t, err, "invalid log level string Inva@Lid3")
70+
}
71+
72+
func TestLoggerSetLogLevel(t *testing.T) {
73+
tempFile, err := ioutil.TempFile(t.TempDir(), "tempFileLoggerTest-")
74+
require.NoError(t, err)
75+
defer tempFile.Close()
76+
77+
Log.Level.SetLevel(zapcore.DebugLevel)
78+
79+
defer Log.Level.SetLevel(zapcore.InfoLevel)
80+
81+
SetLogOutputPaths([]string{tempFile.Name()})
82+
defer SetLogOutputPaths([]string{"stderr"})
83+
84+
Log.Debugf("%s", "logger-test-trace")
85+
tempFileContents, err := ioutil.ReadFile(tempFile.Name())
86+
require.NoError(t, err)
87+
assert.Regexp(t, `{"log.level":"debug","@timestamp":".*","log.origin":{"file.name":"extension/logger_test.go","file.line":.*},"message":"logger-test-trace","ecs.version":"1.6.0"}`, string(tempFileContents))
88+
}
89+
90+
func TestLoggerSetOffLevel(t *testing.T) {
91+
tempFile, err := ioutil.TempFile(t.TempDir(), "tempFileLoggerTest-")
92+
require.NoError(t, err)
93+
defer tempFile.Close()
94+
95+
offLevel, _ := ParseLogLevel("off")
96+
Log.Level.SetLevel(offLevel)
97+
98+
defer Log.Level.SetLevel(zapcore.InfoLevel)
99+
100+
SetLogOutputPaths([]string{tempFile.Name()})
101+
defer SetLogOutputPaths([]string{"stderr"})
102+
103+
Log.Errorf("%s", "logger-test-trace")
104+
tempFileContents, err := ioutil.ReadFile(tempFile.Name())
105+
require.NoError(t, err)
106+
assert.Equal(t, "", string(tempFileContents))
107+
}

apm-lambda-extension/extension/process_env.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@ package extension
1919

2020
import (
2121
"fmt"
22+
"go.uber.org/zap/zapcore"
2223
"os"
2324
"strconv"
2425
"strings"
25-
26-
"github.com/sirupsen/logrus"
2726
)
2827

2928
type extensionConfig struct {
@@ -34,7 +33,7 @@ type extensionConfig struct {
3433
SendStrategy SendStrategy
3534
dataReceiverTimeoutSeconds int
3635
DataForwarderTimeoutSeconds int
37-
LogLevel logrus.Level
36+
LogLevel zapcore.Level
3837
}
3938

4039
// SendStrategy represents the type of sending strategy the extension uses
@@ -83,9 +82,9 @@ func ProcessEnv() *extensionConfig {
8382
normalizedApmLambdaServer = normalizedApmLambdaServer + "/"
8483
}
8584

86-
logLevel, err := logrus.ParseLevel(os.Getenv("ELASTIC_APM_LOG_LEVEL"))
85+
logLevel, err := ParseLogLevel(os.Getenv("ELASTIC_APM_LOG_LEVEL"))
8786
if err != nil {
88-
logLevel = logrus.InfoLevel
87+
logLevel = zapcore.InfoLevel
8988
Log.Warnf("Could not read ELASTIC_APM_LOG_LEVEL, defaulting to %s", logLevel)
9089
}
9190

@@ -111,10 +110,10 @@ func ProcessEnv() *extensionConfig {
111110
config.dataReceiverServerPort = ":8200"
112111
}
113112
if config.apmServerUrl == "" {
114-
Log.Fatalln("please set ELASTIC_APM_LAMBDA_APM_SERVER, exiting")
113+
Log.Fatal("please set ELASTIC_APM_LAMBDA_APM_SERVER, exiting")
115114
}
116115
if config.apmServerSecretToken == "" && config.apmServerApiKey == "" {
117-
Log.Fatalln("please set ELASTIC_APM_SECRET_TOKEN or ELASTIC_APM_API_KEY, exiting")
116+
Log.Fatal("please set ELASTIC_APM_SECRET_TOKEN or ELASTIC_APM_API_KEY, exiting")
118117
}
119118

120119
return config

apm-lambda-extension/extension/process_env_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
package extension
1919

2020
import (
21-
"github.com/sirupsen/logrus"
21+
"go.uber.org/zap/zapcore"
2222
"os"
2323
"testing"
2424
)
@@ -163,7 +163,7 @@ func TestProcessEnv(t *testing.T) {
163163
return
164164
}
165165
config = ProcessEnv()
166-
if config.LogLevel != logrus.DebugLevel {
166+
if config.LogLevel != zapcore.DebugLevel {
167167
t.Log("Log level not set correctly")
168168
t.Fail()
169169
}
@@ -173,7 +173,7 @@ func TestProcessEnv(t *testing.T) {
173173
return
174174
}
175175
config = ProcessEnv()
176-
if config.LogLevel != logrus.InfoLevel {
176+
if config.LogLevel != zapcore.InfoLevel {
177177
t.Log("Log level not set correctly")
178178
t.Fail()
179179
}

apm-lambda-extension/go.mod

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ go 1.17
55
require (
66
github.com/google/uuid v1.3.0
77
github.com/joho/godotenv v1.4.0
8+
github.com/magefile/mage v1.13.0 // indirect
89
github.com/pkg/errors v0.9.1
9-
github.com/sirupsen/logrus v1.8.1
1010
github.com/stretchr/testify v1.7.0
11-
go.elastic.co/ecslogrus v1.0.0
11+
go.elastic.co/ecszap v1.0.1
12+
go.uber.org/atomic v1.9.0 // indirect
13+
go.uber.org/multierr v1.8.0 // indirect
14+
go.uber.org/zap v1.21.0
1215
gotest.tools v2.2.0+incompatible
1316
)
1417

1518
require (
1619
github.com/davecgh/go-spew v1.1.1 // indirect
1720
github.com/google/go-cmp v0.5.6 // indirect
18-
github.com/magefile/mage v1.9.0 // indirect
1921
github.com/pmezard/go-difflib v1.0.0 // indirect
20-
golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 // indirect
21-
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
22-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
22+
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
2323
)

0 commit comments

Comments
 (0)