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

mendersoftware / mender-server / 1833359013

23 May 2025 01:25PM UTC coverage: 66.318% (+0.5%) from 65.861%
1833359013

Pull #674

gitlab-ci

mzedel
fix(gui): prevented device tag editor to be shown when no tags exist

- this is to reduce confusion about tags defined async to the current session not being visible

Ticket: ME-528
Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #674: ME-529, ME-528 - adjustments to device tag editing

29554 of 44564 relevant lines covered (66.32%)

1.45 hits per line

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

78.35
/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 {
4✔
39
        var b strings.Builder
4✔
40
        lastIdx := len(params) - 1
4✔
41
        delimiter := " "
4✔
42
        for i, p := range params {
8✔
43
                if i == lastIdx {
8✔
44
                        delimiter = ""
4✔
45
                }
4✔
46
                key := strings.TrimPrefix(p.Key, ":")
4✔
47
                fmt.Fprintf(&b, "%s=%s%s", key, p.Value, delimiter)
4✔
48
        }
49
        return b.String()
4✔
50
}
51

52
func (a AccessLogger) LogFunc(
53
        ctx context.Context,
54
        c *gin.Context,
55
        startTime time.Time,
56
) {
4✔
57
        logCtx := logrus.Fields{
4✔
58
                "method":      c.Request.Method,
4✔
59
                "path":        c.FullPath(),
4✔
60
                "path_params": formatPathParams(c.Params),
4✔
61
                "qs":          c.Request.URL.RawQuery,
4✔
62
                "ts": startTime.
4✔
63
                        Truncate(time.Millisecond).
4✔
64
                        Format(time.RFC3339Nano),
4✔
65
                "type":      c.Request.Proto,
4✔
66
                "useragent": c.Request.UserAgent(),
4✔
67
        }
4✔
68
        if a.ClientIPHook != nil {
4✔
69
                logCtx["clientip"] = a.ClientIPHook(c.Request)
×
70
        }
×
71
        lc := fromContext(ctx)
4✔
72
        if lc != nil {
8✔
73
                lc.addFields(logCtx)
4✔
74
        }
4✔
75
        if r := recover(); r != nil {
4✔
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) {
4✔
90
                return
×
91
        }
×
92
        latency := time.Since(startTime)
4✔
93
        // We do not need more than 3 digit fraction
4✔
94
        if latency > time.Second {
5✔
95
                latency = latency.Round(time.Millisecond)
1✔
96
        } else if latency > time.Millisecond {
9✔
97
                latency = latency.Round(time.Microsecond)
4✔
98
        }
4✔
99
        code := c.Writer.Status()
4✔
100
        select {
4✔
101
        case <-ctx.Done():
×
102
                if errors.Is(ctx.Err(), context.Canceled) {
×
103
                        code = StatusClientClosedConnection
×
104
                }
×
105
        default:
4✔
106
        }
107
        logCtx["responsetime"] = latency.String()
4✔
108
        logCtx["status"] = c.Writer.Status()
4✔
109
        logCtx["byteswritten"] = c.Writer.Size()
4✔
110

4✔
111
        var logLevel logrus.Level = logrus.InfoLevel
4✔
112
        if code >= 500 {
4✔
113
                logLevel = logrus.ErrorLevel
×
114
        } else if code >= 400 {
7✔
115
                logLevel = logrus.WarnLevel
3✔
116
        }
3✔
117
        if len(c.Errors) > 0 {
6✔
118
                errs := c.Errors.Errors()
2✔
119
                var errMsg string
2✔
120
                if len(errs) == 1 {
4✔
121
                        errMsg = errs[0]
2✔
122
                } else {
3✔
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
2✔
130
        }
131
        log.FromContext(c.Request.Context()).
4✔
132
                WithFields(logCtx).
4✔
133
                Log(logLevel)
4✔
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) {
4✔
141
        ctx := c.Request.Context()
4✔
142
        startTime := time.Now()
4✔
143
        ctx = log.WithContext(ctx, log.New(log.Ctx{}))
4✔
144
        ctx = withContext(ctx, &logContext{maxErrors: DefaultMaxErrors})
4✔
145
        c.Request = c.Request.WithContext(ctx)
4✔
146
        defer a.LogFunc(ctx, c, startTime)
4✔
147
        c.Next()
4✔
148
}
4✔
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 {
4✔
158
        return AccessLogger{ClientIPHook: getClientIPFromEnv()}.Middleware
4✔
159
}
4✔
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