Skip to content

Commit cfb4dc9

Browse files
authored
feat: structured logging in digger (backend) (#1927)
* feat: add structured logging in backend
1 parent 28a8ecd commit cfb4dc9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+4462
-1021
lines changed

backend/bootstrap/main.go

+11-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"html/template"
77
"io/fs"
88
"log"
9+
"log/slog"
910
"net/http"
1011
"os"
1112
"path/filepath"
@@ -59,12 +60,12 @@ func periodicProfiling() {
5960
memProfilePath := filepath.Join("/tmp/profiles", fmt.Sprintf("memory-%s.pprof", timestamp))
6061
f, err := os.Create(memProfilePath)
6162
if err != nil {
62-
log.Printf("Failed to create memory profile: %v", err)
63+
slog.Error("Failed to create memory profile", "error", err)
6364
continue
6465
}
6566

6667
if err := pprof.WriteHeapProfile(f); err != nil {
67-
log.Printf("Failed to write memory profile: %v", err)
68+
slog.Error("Failed to write memory profile", "error", err)
6869
}
6970
f.Close()
7071

@@ -77,7 +78,7 @@ func periodicProfiling() {
7778
func cleanupOldProfiles(dir string, keep int) {
7879
files, err := filepath.Glob(filepath.Join(dir, "memory-*.pprof"))
7980
if err != nil {
80-
log.Printf("Failed to list profile files: %v", err)
81+
slog.Error("Failed to list profile files", "error", err)
8182
return
8283
}
8384

@@ -88,7 +89,7 @@ func cleanupOldProfiles(dir string, keep int) {
8889
// Sort files by name (which includes timestamp)
8990
for i := 0; i < len(files)-keep; i++ {
9091
if err := os.Remove(files[i]); err != nil {
91-
log.Printf("Failed to remove old profile %s: %v", files[i], err)
92+
slog.Error("Failed to remove old profile", "file", files[i], "error", err)
9293
}
9394
}
9495
}
@@ -108,7 +109,7 @@ func Bootstrap(templates embed.FS, diggerController controllers.DiggerController
108109
Release: "api@" + Version,
109110
Debug: true,
110111
}); err != nil {
111-
log.Printf("Sentry initialization failed: %v\n", err)
112+
slog.Error("Sentry initialization failed", "error", err)
112113
}
113114

114115
//database migrations
@@ -241,7 +242,9 @@ func Bootstrap(templates embed.FS, diggerController controllers.DiggerController
241242
}
242243

243244
func initLogging() {
244-
log.SetOutput(os.Stdout)
245-
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
246-
log.Println("Initialized the logger successfully")
245+
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
246+
Level: slog.LevelInfo,
247+
})
248+
logger := slog.New(handler)
249+
slog.SetDefault(logger)
247250
}

backend/ci_backends/github_actions.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"log/slog"
8+
79
"github.com/diggerhq/digger/backend/utils"
810
orchestrator_scheduler "github.com/diggerhq/digger/libs/scheduler"
911
"github.com/diggerhq/digger/libs/spec"
1012
"github.com/google/go-github/v61/github"
11-
"log"
1213
)
1314

1415
type GithubActionCi struct {
1516
Client *github.Client
1617
}
1718

1819
func (g GithubActionCi) TriggerWorkflow(spec spec.Spec, runName string, vcsToken string) error {
19-
log.Printf("TriggerGithubWorkflow: repoOwner: %v, repoName: %v, commentId: %v", spec.VCS.RepoOwner, spec.VCS.RepoName, spec.CommentId)
20+
slog.Info("TriggerGithubWorkflow", "repoOwner", spec.VCS.RepoOwner, "repoName", spec.VCS.RepoName, "commentId", spec.CommentId)
2021
client := g.Client
2122
specBytes, err := json.Marshal(spec)
2223

@@ -35,13 +36,13 @@ func (g GithubActionCi) TriggerWorkflow(spec spec.Spec, runName string, vcsToken
3536

3637
func (g GithubActionCi) GetWorkflowUrl(spec spec.Spec) (string, error) {
3738
if spec.JobId == "" {
38-
log.Printf("Cannot get workflow URL: JobId is empty")
39+
slog.Error("Cannot get workflow URL: JobId is empty")
3940
return "", fmt.Errorf("job ID is required to fetch workflow URL")
4041
}
4142

4243
_, workflowRunUrl, err := utils.GetWorkflowIdAndUrlFromDiggerJobId(g.Client, spec.VCS.RepoOwner, spec.VCS.RepoName, spec.JobId)
4344
if err != nil {
44-
log.Printf("Error getting workflow ID from job: %v", err)
45+
slog.Error("Error getting workflow ID from job", "error", err)
4546
return "", err
4647
} else {
4748
return workflowRunUrl, nil

backend/ci_backends/provider.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ package ci_backends
22

33
import (
44
"fmt"
5+
"log/slog"
6+
57
"github.com/diggerhq/digger/backend/utils"
6-
"log"
78
)
89

910
type CiBackendProvider interface {
@@ -15,7 +16,7 @@ type DefaultBackendProvider struct{}
1516
func (d DefaultBackendProvider) GetCiBackend(options CiBackendOptions) (CiBackend, error) {
1617
client, _, err := utils.GetGithubClientFromAppId(options.GithubClientProvider, options.GithubInstallationId, options.GithubAppId, options.RepoFullName)
1718
if err != nil {
18-
log.Printf("GetCiBackend: could not get github client: %v", err)
19+
slog.Error("GetCiBackend: could not get github client", "error", err)
1920
return nil, fmt.Errorf("could not get github client: %v", err)
2021
}
2122
backend := &GithubActionCi{

backend/controllers/cache.go

+19-19
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ package controllers
22

33
import (
44
"fmt"
5-
"github.com/diggerhq/digger/backend/models"
6-
"github.com/diggerhq/digger/backend/utils"
7-
dg_configuration "github.com/diggerhq/digger/libs/digger_config"
8-
"github.com/gin-gonic/gin"
9-
"log"
5+
"log/slog"
106
"net/http"
117
"os"
128
"path"
139
"strings"
10+
11+
"github.com/diggerhq/digger/backend/models"
12+
"github.com/diggerhq/digger/backend/utils"
13+
dg_configuration "github.com/diggerhq/digger/libs/digger_config"
14+
"github.com/gin-gonic/gin"
1415
)
1516

1617
func (d DiggerController) UpdateRepoCache(c *gin.Context) {
@@ -24,7 +25,7 @@ func (d DiggerController) UpdateRepoCache(c *gin.Context) {
2425
var request UpdateCacheRequest
2526
err := c.BindJSON(&request)
2627
if err != nil {
27-
log.Printf("Error binding JSON: %v", err)
28+
slog.Error("Error binding JSON", "error", err)
2829
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error binding JSON"})
2930
return
3031
}
@@ -33,22 +34,21 @@ func (d DiggerController) UpdateRepoCache(c *gin.Context) {
3334
installationId := request.InstallationId
3435
link, err := models.DB.GetGithubAppInstallationLink(installationId)
3536
if err != nil {
36-
log.Printf("could not installation link: %v", err)
37-
c.String(500, fmt.Sprintf("coulnt not find installation link %v %v", repoFullName, installationId))
37+
slog.Error("Could not get installation link", "error", err, "repoFullName", repoFullName, "installationId", installationId)
38+
c.String(http.StatusInternalServerError, fmt.Sprintf("could not find installation link %v %v", repoFullName, installationId))
3839
return
39-
4040
}
4141
orgId := link.OrganisationId
4242

43-
log.Printf("the org id is %v", orgId)
43+
slog.Info("Processing repo cache update", "orgId", orgId)
4444

4545
repoOwner, repoName, _ := strings.Cut(repoFullName, "/")
4646
repoDiggerName := strings.ReplaceAll(repoFullName, "/", "-")
4747

4848
repo, err := models.DB.GetRepo(orgId, repoDiggerName)
4949
if err != nil {
50-
log.Printf("could not get repo: %v", err)
51-
c.String(500, fmt.Sprintf("coulnt not get repository %v %v", repoFullName, orgId))
50+
slog.Error("Could not get repo", "error", err, "repoFullName", repoFullName, "orgId", orgId)
51+
c.String(http.StatusInternalServerError, fmt.Sprintf("could not get repository %v %v", repoFullName, orgId))
5252
return
5353
}
5454

@@ -57,8 +57,8 @@ func (d DiggerController) UpdateRepoCache(c *gin.Context) {
5757

5858
_, token, err := utils.GetGithubService(d.GithubClientProvider, installationId, repoFullName, repoOwner, repoName)
5959
if err != nil {
60-
log.Printf("could not get github service :%v", err)
61-
c.String(500, fmt.Sprintf("could not get github service %v %v", repoFullName, orgId))
60+
slog.Error("Could not get GitHub service", "error", err, "repoFullName", repoFullName, "orgId", orgId)
61+
c.String(http.StatusInternalServerError, fmt.Sprintf("could not get github service %v %v", repoFullName, orgId))
6262
return
6363
}
6464

@@ -72,23 +72,23 @@ func (d DiggerController) UpdateRepoCache(c *gin.Context) {
7272
diggerYmlStr = string(diggerYmlBytes)
7373
config, _, _, err = dg_configuration.LoadDiggerConfig(dir, true, nil)
7474
if err != nil {
75-
log.Printf("Error loading digger config: %v", err)
75+
slog.Error("Error loading digger config", "error", err)
7676
return err
7777
}
7878
return nil
7979
})
8080

8181
if err != nil {
82-
log.Printf("could not load digger config :%v", err)
82+
slog.Error("Could not load digger config", "error", err)
8383
return
8484
}
8585
_, err = models.DB.UpsertRepoCache(orgId, repoFullName, diggerYmlStr, *config)
8686
if err != nil {
87-
log.Printf("could upadate repo cache :%v", err)
87+
slog.Error("Could not update repo cache", "error", err)
8888
return
89-
9089
}
90+
slog.Info("Successfully updated repo cache", "repoFullName", repoFullName, "orgId", orgId)
9191
}()
9292

93-
c.String(200, "successfully submitted cache for processing, check backend logs for progress")
93+
c.String(http.StatusOK, "successfully submitted cache for processing, check backend logs for progress")
9494
}

backend/controllers/connections.go

+25-15
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package controllers
22

33
import (
44
"errors"
5-
"github.com/samber/lo"
6-
"log"
5+
"log/slog"
76
"net/http"
87
"os"
98

9+
"github.com/samber/lo"
10+
1011
"github.com/diggerhq/digger/backend/utils"
1112
"gorm.io/gorm"
1213

@@ -22,15 +23,15 @@ func ListVCSConnectionsApi(c *gin.Context) {
2223
var org models.Organisation
2324
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
2425
if err != nil {
25-
log.Printf("could not fetch organisation: %v err: %v", organisationId, err)
26+
slog.Error("Could not fetch organisation", "organisationId", organisationId, "error", err)
2627
c.JSON(http.StatusNotFound, gin.H{"error": "Could not fetch organisation"})
2728
return
2829
}
2930

3031
var connections []models.VCSConnection
3132
err = models.DB.GormDB.Where("organisation_id = ?", org.ID).Find(&connections).Error
3233
if err != nil {
33-
log.Printf("could not fetch VCS connections: %v", err)
34+
slog.Error("Could not fetch VCS connections", "organisationId", organisationId, "error", err)
3435
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not fetch VCS connections"})
3536
return
3637
}
@@ -54,7 +55,7 @@ func CreateVCSConnectionApi(c *gin.Context) {
5455
var org models.Organisation
5556
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
5657
if err != nil {
57-
log.Printf("could not fetch organisation: %v err: %v", organisationId, err)
58+
slog.Error("Could not fetch organisation", "organisationId", organisationId, "error", err)
5859
c.JSON(http.StatusNotFound, gin.H{"error": "Could not fetch organisation"})
5960
return
6061
}
@@ -68,33 +69,34 @@ func CreateVCSConnectionApi(c *gin.Context) {
6869

6970
var request CreateVCSConnectionRequest
7071
if err := c.BindJSON(&request); err != nil {
72+
slog.Error("Invalid request body", "error", err)
7173
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
7274
return
7375
}
7476

7577
if request.VCS != "bitbucket" {
76-
log.Printf("VCS type not supported: %v", request.VCS)
78+
slog.Error("VCS type not supported", "type", request.VCS)
7779
c.JSON(http.StatusBadRequest, gin.H{"error": "VCS type not supported"})
7880
return
7981
}
8082

8183
secret := os.Getenv("DIGGER_ENCRYPTION_SECRET")
8284
if secret == "" {
83-
log.Printf("ERROR: no encryption secret specified")
85+
slog.Error("No encryption secret specified")
8486
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not encrypt access token"})
8587
return
8688
}
8789

8890
bitbucketAccessTokenEncrypted, err := utils.AESEncrypt([]byte(secret), request.BitbucketAccessToken)
8991
if err != nil {
90-
log.Printf("could not encrypt access token: %v", err)
92+
slog.Error("Could not encrypt access token", "error", err)
9193
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not encrypt access token"})
9294
return
9395
}
9496

9597
bitbucketWebhookSecretEncrypted, err := utils.AESEncrypt([]byte(secret), request.BitbucketWebhookSecret)
9698
if err != nil {
97-
log.Printf("could not encrypt webhook secret: %v", err)
99+
slog.Error("Could not encrypt webhook secret", "error", err)
98100
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not encrypt webhook secret"})
99101
return
100102
}
@@ -114,9 +116,12 @@ func CreateVCSConnectionApi(c *gin.Context) {
114116
org.ID,
115117
)
116118
if err != nil {
117-
log.Printf("")
119+
slog.Error("Could not create VCS connection", "error", err)
120+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create VCS connection"})
121+
return
118122
}
119123

124+
slog.Info("Created VCS connection", "connectionId", connection.ID, "organisationId", org.ID)
120125
c.JSON(http.StatusCreated, gin.H{
121126
"connection": connection.ID,
122127
})
@@ -131,9 +136,10 @@ func GetVCSConnection(c *gin.Context) {
131136
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
132137
if err != nil {
133138
if errors.Is(err, gorm.ErrRecordNotFound) {
139+
slog.Info("Organisation not found", "organisationId", organisationId)
134140
c.String(http.StatusNotFound, "Could not find organisation: "+organisationId)
135141
} else {
136-
log.Printf("could not fetch organisation: %v err: %v", organisationId, err)
142+
slog.Error("Could not fetch organisation", "organisationId", organisationId, "error", err)
137143
c.String(http.StatusNotFound, "Could not fetch organisation: "+organisationId)
138144
}
139145
return
@@ -143,9 +149,10 @@ func GetVCSConnection(c *gin.Context) {
143149
err = models.DB.GormDB.Where("id = ? AND organisation_id = ?", connectionId, org.ID).First(&connection).Error
144150
if err != nil {
145151
if errors.Is(err, gorm.ErrRecordNotFound) {
152+
slog.Info("Connection not found", "connectionId", connectionId, "organisationId", org.ID)
146153
c.String(http.StatusNotFound, "Could not find connection: "+connectionId)
147154
} else {
148-
log.Printf("could not fetch connection: %v err: %v", connectionId, err)
155+
slog.Error("Could not fetch connection", "connectionId", connectionId, "error", err)
149156
c.String(http.StatusInternalServerError, "Could not fetch connection")
150157
}
151158
return
@@ -166,9 +173,10 @@ func DeleteVCSConnection(c *gin.Context) {
166173
err := models.DB.GormDB.Where("external_id = ? AND external_source = ?", organisationId, organisationSource).First(&org).Error
167174
if err != nil {
168175
if errors.Is(err, gorm.ErrRecordNotFound) {
176+
slog.Info("Organisation not found", "organisationId", organisationId)
169177
c.String(http.StatusNotFound, "Could not find organisation: "+organisationId)
170178
} else {
171-
log.Printf("could not fetch organisation: %v err: %v", organisationId, err)
179+
slog.Error("Could not fetch organisation", "organisationId", organisationId, "error", err)
172180
c.String(http.StatusNotFound, "Could not fetch organisation: "+organisationId)
173181
}
174182
return
@@ -178,21 +186,23 @@ func DeleteVCSConnection(c *gin.Context) {
178186
err = models.DB.GormDB.Where("id = ? AND organisation_id = ?", connectionId, org.ID).First(&connection).Error
179187
if err != nil {
180188
if errors.Is(err, gorm.ErrRecordNotFound) {
189+
slog.Info("Connection not found", "connectionId", connectionId, "organisationId", org.ID)
181190
c.String(http.StatusNotFound, "Could not find connection: "+connectionId)
182191
} else {
183-
log.Printf("could not fetch connection: %v err: %v", connectionId, err)
192+
slog.Error("Could not fetch connection", "connectionId", connectionId, "error", err)
184193
c.String(http.StatusInternalServerError, "Could not fetch connection")
185194
}
186195
return
187196
}
188197

189198
err = models.DB.GormDB.Delete(&connection).Error
190199
if err != nil {
191-
log.Printf("could not delete connection: %v err: %v", connectionId, err)
200+
slog.Error("Could not delete connection", "connectionId", connectionId, "error", err)
192201
c.String(http.StatusInternalServerError, "Could not delete connection")
193202
return
194203
}
195204

205+
slog.Info("Successfully deleted VCS connection", "connectionId", connectionId, "organisationId", org.ID)
196206
c.JSON(http.StatusOK, gin.H{
197207
"status": "success",
198208
})

0 commit comments

Comments
 (0)