• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-server / 1863989307

11 Jun 2025 08:38AM UTC coverage: 66.403% (+0.7%) from 65.731%
1863989307

Pull #720

gitlab-ci

mzedel
test(gui): made e2e tests work with changed tenant token expansion

Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #720: MEN-8411 - billing/ organization split

29581 of 44548 relevant lines covered (66.4%)

1.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.38
/backend/pkg/accesslog/middleware_gin.go
1
// Copyright 2024 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package accesslog
16

17
import (
18
        "context"
19
        "fmt"
20
        "net"
21
        "net/http"
22
        "strings"
23
        "time"
24

25
        "github.com/gin-gonic/gin"
26
        "github.com/pkg/errors"
27
        "github.com/sirupsen/logrus"
28

29
        "github.com/mendersoftware/mender-server/pkg/log"
30
        "github.com/mendersoftware/mender-server/pkg/rest.utils"
31
)
32

33
type AccessLogger struct {
34
        DisableLog   func(c *gin.Context) bool
35
        ClientIPHook func(r *http.Request) net.IP
36
}
37

38
func formatPathParams(params gin.Params) string {
6✔
39
        var b strings.Builder
6✔
40
        lastIdx := len(params) - 1
6✔
41
        delimiter := " "
6✔
42
        for i, p := range params {
12✔
43
                if i == lastIdx {
12✔
44
                        delimiter = ""
6✔
45
                }
6✔
46
                key := strings.TrimPrefix(p.Key, ":")
6✔
47
                fmt.Fprintf(&b, "%s=%s%s", key, p.Value, delimiter)
6✔
48
        }
49
        return b.String()
6✔
50
}
51

52
func (a AccessLogger) LogFunc(
53
        ctx context.Context,
54
        c *gin.Context,
55
        startTime time.Time,
56
) {
6✔
57
        logCtx := logrus.Fields{
6✔
58
                "method":      c.Request.Method,
6✔
59
                "path":        c.FullPath(),
6✔
60
                "path_params": formatPathParams(c.Params),
6✔
61
                "qs":          c.Request.URL.RawQuery,
6✔
62
                "ts": startTime.
6✔
63
                        Truncate(time.Millisecond).
6✔
64
                        Format(time.RFC3339Nano),
6✔
65
                "type":      c.Request.Proto,
6✔
66
                "useragent": c.Request.UserAgent(),
6✔
67
        }
6✔
68
        if a.ClientIPHook != nil {
6✔
69
                logCtx["clientip"] = a.ClientIPHook(c.Request)
×
70
        }
×
71
        lc := fromContext(ctx)
6✔
72
        if lc != nil {
12✔
73
                lc.addFields(logCtx)
6✔
74
        }
6✔
75
        if r := recover(); r != nil {
6✔
76
                trace := collectTrace()
×
77
                logCtx["trace"] = trace
×
78
                logCtx["panic"] = r
×
79

×
80
                func() {
×
81
                        // Try to respond with an internal server error.
×
82
                        // If the connection is broken it might panic again.
×
83
                        defer func() { recover() }() // nolint:errcheck
×
84
                        rest.RenderError(c,
×
85
                                http.StatusInternalServerError,
×
86
                                errors.New("internal error"),
×
87
                        )
×
88
                }()
89
        } else if a.DisableLog != nil && a.DisableLog(c) {
6✔
90
                return
×
91
        }
×
92
        latency := time.Since(startTime)
6✔
93
        // We do not need more than 3 digit fraction
6✔
94
        if latency > time.Second {
7✔
95
                latency = latency.Round(time.Millisecond)
1✔
96
        } else if latency > time.Millisecond {
12✔
97
                latency = latency.Round(time.Microsecond)
5✔
98
        }
5✔
99
        code := c.Writer.Status()
6✔
100
        select {
6✔
101
        case <-ctx.Done():
×
102
                if errors.Is(ctx.Err(), context.Canceled) {
×
103
                        code = StatusClientClosedConnection
×
104
                }
×
105
        default:
6✔
106
        }
107
        logCtx["responsetime"] = latency.String()
6✔
108
        logCtx["status"] = c.Writer.Status()
6✔
109
        logCtx["byteswritten"] = c.Writer.Size()
6✔
110

6✔
111
        var logLevel logrus.Level = logrus.InfoLevel
6✔
112
        if code >= 500 {
7✔
113
                logLevel = logrus.ErrorLevel
1✔
114
        } else if code >= 400 {
12✔
115
                logLevel = logrus.WarnLevel
5✔
116
        }
5✔
117
        if len(c.Errors) > 0 {
10✔
118
                errs := c.Errors.Errors()
4✔
119
                var errMsg string
4✔
120
                if len(errs) == 1 {
8✔
121
                        errMsg = errs[0]
4✔
122
                } else {
5✔
123
                        for i, err := range errs {
2✔
124
                                errMsg = errMsg + fmt.Sprintf(
1✔
125
                                        "#%02d: %s\n", i+1, err,
1✔
126
                                )
1✔
127
                        }
1✔
128
                }
129
                logCtx["error"] = errMsg
4✔
130
        }
131
        log.FromContext(c.Request.Context()).
6✔
132
                WithFields(logCtx).
6✔
133
                Log(logLevel)
6✔
134
}
135

136
// Middleware implementsa gin compatible MiddlewareFunc
137
//
138
// NOTE: This accesslog middleware also implements the legacy requestlog
139
// middleware.
140
func (a AccessLogger) Middleware(c *gin.Context) {
6✔
141
        ctx := c.Request.Context()
6✔
142
        startTime := time.Now()
6✔
143
        ctx = log.WithContext(ctx, log.New(log.Ctx{}))
6✔
144
        ctx = withContext(ctx, &logContext{maxErrors: DefaultMaxErrors})
6✔
145
        c.Request = c.Request.WithContext(ctx)
6✔
146
        defer a.LogFunc(ctx, c, startTime)
6✔
147
        c.Next()
6✔
148
}
6✔
149

150
// Middleware provides accesslog middleware for the gin-gonic framework.
151
// This middleware will recover any panic from occurring in the API
152
// handler and log it to error level with panic and trace showing the panic
153
// message and traceback respectively.
154
// If an error status is returned in the response, the middleware tries
155
// to pop the topmost error from the gin.Context (c.Error) and puts it in
156
// the "error" context to the final log entry.
157
func Middleware() gin.HandlerFunc {
6✔
158
        return AccessLogger{ClientIPHook: getClientIPFromEnv()}.Middleware
6✔
159
}
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc