> 백엔드 개발 > Golang > Emacs에서 Golang 디버깅 마스터하기

Emacs에서 Golang 디버깅 마스터하기

Linda Hamilton
풀어 주다: 2024-12-02 11:56:09
원래의
209명이 탐색했습니다.

소개

저는 Golang으로 개발을 시작한 이후로 디버거를 실제로 사용하지 않았습니다. 대신 나는 내 코드를 검증하기 위해 순진하게 fmt.Print 문을 모든 곳에 추가했습니다. 인쇄 문과 로그는 첫 번째 디버깅 본능일 수도 있지만 정교한 런타임 동작과 (물론!) 재현이 불가능해 보이는 복잡한 동시성 문제로 인해 크고 복잡한 코드 기반을 처리할 때 종종 부족합니다.

보다 복잡한 프로젝트 작업을 시작한 후(예: https://github.com/cloudoperators/heureka) delve(Golang 디버거)에 대해 더 자세히 살펴보고 Emacs가 무엇을 제공하는지 확인해야 했습니다. 그것과 상호 작용합니다. Go 생태계는 뛰어난 디버깅 도구를 제공하지만 이를 편안한 개발 워크플로에 통합하는 것은 어려울 수 있습니다.

이번 게시물에서는 Emacs, Delve 및 dape의 강력한 조합에 대해 자세히 설명하겠습니다. 이러한 도구를 함께 사용하면 Emacs의 유명한 유연성과 확장성을 유지하면서 기존 IDE를 모방하는(종종 능가하는) 디버깅 환경을 만들 수 있습니다.

다음과 같은 결과를 기대할 수 있습니다.

  • dape를 사용하여 Delve 설정 및 구성
  • 표준 애플리케이션과 Ginkgo 테스트를 모두 디버그합니다(현재 제가 사용하고 있는 것이 이것이죠?)
  • Emacs 전용 사용자 정의로 디버깅 작업 흐름을 최적화하세요

개발 환경 설정

이 게시물에서는 여러분이 이미 Emacs 경험을 갖고 있으며 이제 패키지를 구성하고 작은 Elisp 스니펫을 작성하는 방법을 알고 있다고 가정합니다. 저는 개인적으로 Straight.el을 패키지 관리자로 사용하고,minimal-emacs.d를 최소 바닐라 Emacs 구성으로 사용하고(나만의 사용자 정의와 함께), dape를 디버그 어댑터 클라이언트로, eglot을 LSP 클라이언트로 사용합니다.

필수 Emacs 패키지

Emacs 29 사용자의 경우 eglot이 내장되어 있습니다. Gopls에 대한 eglot 구성 및 일부 고급 Gopls 설정을 확인하세요. 먼저 dape를 추가하겠습니다.

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

고 모드:

(use-package go-mode
  :straight t
  :mode "\.go\'"
  :hook ((before-save . gofmt-before-save))
  :bind (:map go-mode-map
              ("M-?" . godoc-at-point)
              ("M-." . xref-find-definitions)
              ("M-_" . xref-find-references)
              ;; ("M-*" . pop-tag-mark) ;; Jump back after godef-jump
              ("C-c m r" . go-run))
  :custom
  (gofmt-command "goimports"))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

필수 Go 도구 설치

LSP 서버인 Delve와 gopls를 설치하세요.

# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Install gopls
go install golang.org/x/tools/gopls@latest
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

또한 제가 가끔 사용하는 다른 도구도 많이 있습니다.

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest

go install -v golang.org/x/tools/cmd/godoc@latest
go install -v golang.org/x/tools/cmd/goimports@latest
go install -v github.com/stamblerre/gocode@latest
go install -v golang.org/x/tools/cmd/gorename@latest
go install -v golang.org/x/tools/cmd/guru@latest
go install -v github.com/cweill/gotests/...@latest

go install -v github.com/davidrjenni/reftools/cmd/fillstruct@latest
go install -v github.com/fatih/gomodifytags@latest
go install -v github.com/godoctor/godoctor@latest
go install -v github.com/haya14busa/gopkgs/cmd/gopkgs@latest
go install -v github.com/josharian/impl@latest
go install -v github.com/rogpeppe/godef@latest
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

그런 다음 해당 Emacs 패키지를 구성해야 합니다.

(use-package ginkgo
  :straight (:type git :host github :repo "garslo/ginkgo-mode")
  :init
  (setq ginkgo-use-pwd-as-test-dir t
        ginkgo-use-default-keys t))

(use-package gotest
  :straight t
  :after go-mode
  :bind (:map go-mode-map
              ("C-c t f" . go-test-current-file)
              ("C-c t t" . go-test-current-test)
              ("C-c t j" . go-test-current-project)
              ("C-c t b" . go-test-current-benchmark)
              ("C-c t c" . go-test-current-coverage)
              ("C-c t x" . go-run)))

(use-package go-guru
  :straight t
  :hook
  (go-mode . go-guru-hl-identifier-mode))

(use-package go-projectile
  :straight t
  :after (projectile go-mode))

(use-package flycheck-golangci-lint
  :straight t
  :hook
  (go-mode . flycheck-golangci-lint-setup))

(use-package go-eldoc
  :straight t
  :hook
  (go-mode . go-eldoc-setup))

(use-package go-tag
  :straight t
  :bind (:map go-mode-map
              ("C-c t a" . go-tag-add)
              ("C-c t r" . go-tag-remove))
  :init (setq go-tag-args (list "-transform" "camelcase")))

(use-package go-fill-struct
  :straight t)

(use-package go-impl
  :straight t)

(use-package go-playground
  :straight t)
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

데이프 구성

dap 대신 dape를 사용하는 특별한 이유는 없습니다. 내가 여전히 MinEmacs를 사용하고 있을 때 그것은 그것의 일부였고 나는 그것에 익숙해졌습니다. 문서에 따르면:

  • Dape는 launch.json 파일을 지원하지 않습니다. 프로젝트별 구성이 필요한 경우 dir-locals 및 dape-command를 사용하세요.
  • Dape는 사용자가 옵션을 사용하여 기존 구성에 PLIST 항목을 수정하거나 추가할 수 있도록 하여 미니버퍼 내의 인체공학성을 향상시킵니다.
  • 마법도 없고 ${workspaceFolder}와 같은 특수 변수도 없습니다. 대신 새 세션을 시작하기 전에 함수와 변수가 해결됩니다.
  • vscode가 존재하지 않는 경우 Emacs에서 디버그 어댑터 구성이 어떻게 구현될지 상상해 봅니다.

VSCode를 사용해 본 적이 있다면 이 VSCode가 다양한 디버깅 프로필을 저장하기 위해 launch.json을 사용한다는 사실을 이미 알고 계실 것입니다.

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

이 페이지에 따라 디버깅 구성에서 조정할 수 있는 다양한 필드/속성이 있습니다.

Property Description
name Name for your configuration that appears in the drop down in the Debug viewlet
type Always set to "go". This is used by VS Code to figure out which extension should be used for debugging your code
request Either of launch or attach. Use attach when you want to attach to an already running process
mode For launch requests, either of auto, debug, remote, test, exec. For attach requests, use either local or remote
program Absolute path to the package or file to debug when in debug & test mode, or to the pre-built binary file to debug in exec mode
env Environment variables to use when debugging. Example: { "ENVNAME": "ENVVALUE" }
envFile Absolute path to a file containing environment variable definitions
args Array of command line arguments that will be passed to the program being debugged
showLog Boolean indicating if logs from delve should be printed in the debug console
logOutput Comma separated list of delve components for debug output
buildFlags Build flags to be passed to the Go compiler
remotePath Absolute path to the file being debugged on the remote machine
processId ID of the process that needs debugging (for attach request with local mode)

샘플 애플리케이션

이제 REST API를 구현한 실제 애플리케이션을 디버깅하여 우리가 배운 내용을 실제로 적용해 보겠습니다.

프로젝트 구조

우리의 예는 다음 구조를 가진 작업 관리용 REST API입니다.

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

핵심 구성요소

핵심 구성요소를 살펴보겠습니다.

작업은 핵심 도메인 모델을 나타냅니다.

(use-package go-mode
  :straight t
  :mode "\.go\'"
  :hook ((before-save . gofmt-before-save))
  :bind (:map go-mode-map
              ("M-?" . godoc-at-point)
              ("M-." . xref-find-definitions)
              ("M-_" . xref-find-references)
              ;; ("M-*" . pop-tag-mark) ;; Jump back after godef-jump
              ("C-c m r" . go-run))
  :custom
  (gofmt-command "goimports"))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

TaskStore는 메모리 내 데이터 작업을 처리합니다.

# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Install gopls
go install golang.org/x/tools/gopls@latest
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

REST API

API는 다음 엔드포인트를 노출합니다.

  • POST /task/create - 새 작업을 생성합니다
  • GET /task/get?id= - ID로 작업을 검색합니다.
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest

go install -v golang.org/x/tools/cmd/godoc@latest
go install -v golang.org/x/tools/cmd/goimports@latest
go install -v github.com/stamblerre/gocode@latest
go install -v golang.org/x/tools/cmd/gorename@latest
go install -v golang.org/x/tools/cmd/guru@latest
go install -v github.com/cweill/gotests/...@latest

go install -v github.com/davidrjenni/reftools/cmd/fillstruct@latest
go install -v github.com/fatih/gomodifytags@latest
go install -v github.com/godoctor/godoctor@latest
go install -v github.com/haya14busa/gopkgs/cmd/gopkgs@latest
go install -v github.com/josharian/impl@latest
go install -v github.com/rogpeppe/godef@latest
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

섬기는 사람

서버 구현은 다음과 같습니다.

(use-package ginkgo
  :straight (:type git :host github :repo "garslo/ginkgo-mode")
  :init
  (setq ginkgo-use-pwd-as-test-dir t
        ginkgo-use-default-keys t))

(use-package gotest
  :straight t
  :after go-mode
  :bind (:map go-mode-map
              ("C-c t f" . go-test-current-file)
              ("C-c t t" . go-test-current-test)
              ("C-c t j" . go-test-current-project)
              ("C-c t b" . go-test-current-benchmark)
              ("C-c t c" . go-test-current-coverage)
              ("C-c t x" . go-run)))

(use-package go-guru
  :straight t
  :hook
  (go-mode . go-guru-hl-identifier-mode))

(use-package go-projectile
  :straight t
  :after (projectile go-mode))

(use-package flycheck-golangci-lint
  :straight t
  :hook
  (go-mode . flycheck-golangci-lint-setup))

(use-package go-eldoc
  :straight t
  :hook
  (go-mode . go-eldoc-setup))

(use-package go-tag
  :straight t
  :bind (:map go-mode-map
              ("C-c t a" . go-tag-add)
              ("C-c t r" . go-tag-remove))
  :init (setq go-tag-args (list "-transform" "camelcase")))

(use-package go-fill-struct
  :straight t)

(use-package go-impl
  :straight t)

(use-package go-playground
  :straight t)
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

주요 기능을 살펴보겠습니다.

{
    "name": "Launch file",
    "type": "go",
    "request": "launch",
    "mode": "auto",
    "program": "${file}"
}
로그인 후 복사
로그인 후 복사

애플리케이션 구축

서버를 시작해 보겠습니다.

taskapi/
├── go.mod
├── go.sum
├── main.go
├── task_store.go
└── task_test.go
로그인 후 복사
로그인 후 복사

이제 다른 터미널에서 새 작업을 생성하세요:

import (
    "fmt"
)

type Task struct {
    ID          int    `json:"id"`
    Title       string `json:"title"`
    Description string `json:"description"`
    Done        bool   `json:"done"`
}
로그인 후 복사
로그인 후 복사

응답:

type TaskStore struct {
    tasks  map[int]Task
    nextID int
}

func NewTaskStore() *TaskStore {
    return &TaskStore{
        tasks:  make(map[int]Task),
        nextID: 1,
    }
}
로그인 후 복사
로그인 후 복사

가져올 수 있는지 살펴보겠습니다.

// CreateTask stores a given Task internally
func (ts *TaskStore) CreateTask(task Task) Task {
    task.ID = ts.nextID
    ts.tasks[task.ID] = task
    ts.nextID++
    return task
}

// GetTask retrieves a Task by ID
func (ts *TaskStore) GetTask(id int) (Task, error) {
    task, exists := ts.tasks[id]
    if !exists {
        return Task{}, fmt.Errorf("task with id %d not found", id)
    }
    return task, nil
}

// UpdateTask updates task ID with a new Task object
func (ts *TaskStore) UpdateTask(id int, task Task) error {
    if _, exists := ts.tasks[id]; !exists {
        return fmt.Errorf("task with id %d not found", id)
    }
    task.ID = id
    ts.tasks[id] = task
    return nil
}
로그인 후 복사
로그인 후 복사

응답:

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

// Server implements a web application for managing tasks
type Server struct {
    store *TaskStore
}

func (s *Server) handleCreateTask(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    var task Task
    if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    createdTask := s.store.CreateTask(task)
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(createdTask)
}

func (s *Server) handleGetTask(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    id := 0
    fmt.Sscanf(r.URL.Query().Get("id"), "%d", &id)

    task, err := s.store.GetTask(id)
    if err != nil {
        http.Error(w, err.Error(), http.StatusNotFound)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(task)
}
로그인 후 복사

단위 테스트

다음은 TaskStore에 대한 몇 가지 단위 테스트(Ginkgo로 작성)입니다.

package main

import (
    "log"
    "net/http"
)

func main() {
    store := NewTaskStore()
    server := &Server{store: store}
    http.HandleFunc("/task/create", server.handleCreateTask)
    http.HandleFunc("/task/get", server.handleGetTask)

    log.Printf("Starting server on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
로그인 후 복사
go build -o taskapi *.go
./taskapi
2024/11/14 07:03:48 Starting server on :8080
로그인 후 복사

Emacs에서는 다음 스크린샷과 같이 ginkgo-run-this-container를 호출합니다.

Mastering Golang Debugging in Emacs

Delve 및 Dape를 사용한 기본 디버깅

Task API를 디버깅하기 위해 다음과 같은 접근 방식이 있습니다.

  • 애플리케이션을 직접 실행하고 디버그할 수 있습니다
  • 실행 중인 프로세스에 연결할 수 있습니다
  • 실행 중인 디버깅 세션에 연결할 수 있습니다

다양한 요청 유형에 대한 옵션은 다음과 같습니다.

요청 모드 필수 선택사항
request mode required optional
launch debug program dlvCwd, env, backend, args, cwd, buildFlags, output, noDebug
test program dlvCwd, env, backend, args, cwd, buildFlags, output, noDebug
exec program dlvCwd, env, backend, args, cwd, noDebug
core program, corefilePath dlvCwd, env
replay traceDirPath dlvCwd, env
attach local processId backend
remote
출시 디버그 프로그램 dlvCwd, env, 백엔드, args, cwd, buildFlags, 출력, noDebug 테스트 프로그램 dlvCwd, env, 백엔드, args, cwd, buildFlags, 출력, noDebug 실행 프로그램 dlvCwd, env, 백엔드, args, cwd, noDebug 코어 프로그램, corefilePath dlvCwd, 환경 재생 traceDirPath dlvCwd, 환경 첨부 로컬 프로세스 ID 백엔드 원격

프로필 1: 애플리케이션 실행

다음은 .dir-locals.el에 대한 첫 번째 디버깅 프로필입니다.

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

? command-cwd에 다른 값을 사용할 수도 있습니다. 내 경우에는 현재 프로젝트가 아닌 디렉터리에서 디버거를 시작하고 싶었습니다. default-directory는 현재 있는 현재 버퍼의 작업 디렉토리를 보유하는 변수입니다.

디버깅 시작:

  • dape-info를 실행하여 디버깅 정보 표시

Mastering Golang Debugging in Emacs

  • dape-breakpoint-toggle을 사용하여 중단점 만들기:

Mastering Golang Debugging in Emacs

이 프로필로 디버거를 시작한 후 dape-repl 버퍼에 다음이 표시되어야 합니다.

(use-package go-mode
  :straight t
  :mode "\.go\'"
  :hook ((before-save . gofmt-before-save))
  :bind (:map go-mode-map
              ("M-?" . godoc-at-point)
              ("M-." . xref-find-definitions)
              ("M-_" . xref-find-references)
              ;; ("M-*" . pop-tag-mark) ;; Jump back after godef-jump
              ("C-c m r" . go-run))
  :custom
  (gofmt-command "goimports"))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

디버깅할 바이너리/파일을 지정하지 않았습니다(.dir-locals.el에 :program "."가 있음). delve는 애플리케이션을 시작하기 전에 바이너리를 자동으로 빌드합니다.

# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Install gopls
go install golang.org/x/tools/gopls@latest
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

프로필 2: 외부 디버거에 연결

기존 디버깅 세션에 연결하기 위한 프로필을 추가해 보겠습니다.

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest

go install -v golang.org/x/tools/cmd/godoc@latest
go install -v golang.org/x/tools/cmd/goimports@latest
go install -v github.com/stamblerre/gocode@latest
go install -v golang.org/x/tools/cmd/gorename@latest
go install -v golang.org/x/tools/cmd/guru@latest
go install -v github.com/cweill/gotests/...@latest

go install -v github.com/davidrjenni/reftools/cmd/fillstruct@latest
go install -v github.com/fatih/gomodifytags@latest
go install -v github.com/godoctor/godoctor@latest
go install -v github.com/haya14busa/gopkgs/cmd/gopkgs@latest
go install -v github.com/josharian/impl@latest
go install -v github.com/rogpeppe/godef@latest
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

이제 CLI에서 디버거를 시작해 보겠습니다.

(use-package ginkgo
  :straight (:type git :host github :repo "garslo/ginkgo-mode")
  :init
  (setq ginkgo-use-pwd-as-test-dir t
        ginkgo-use-default-keys t))

(use-package gotest
  :straight t
  :after go-mode
  :bind (:map go-mode-map
              ("C-c t f" . go-test-current-file)
              ("C-c t t" . go-test-current-test)
              ("C-c t j" . go-test-current-project)
              ("C-c t b" . go-test-current-benchmark)
              ("C-c t c" . go-test-current-coverage)
              ("C-c t x" . go-run)))

(use-package go-guru
  :straight t
  :hook
  (go-mode . go-guru-hl-identifier-mode))

(use-package go-projectile
  :straight t
  :after (projectile go-mode))

(use-package flycheck-golangci-lint
  :straight t
  :hook
  (go-mode . flycheck-golangci-lint-setup))

(use-package go-eldoc
  :straight t
  :hook
  (go-mode . go-eldoc-setup))

(use-package go-tag
  :straight t
  :bind (:map go-mode-map
              ("C-c t a" . go-tag-add)
              ("C-c t r" . go-tag-remove))
  :init (setq go-tag-args (list "-transform" "camelcase")))

(use-package go-fill-struct
  :straight t)

(use-package go-impl
  :straight t)

(use-package go-playground
  :straight t)
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

이제 Emacs 내에서 dape를 실행하고 go-attach-taskapi 프로필을 선택할 수 있습니다.

Mastering Golang Debugging in Emacs

프로필 3: 실행 중인 프로세스에 연결

이 시나리오에서는 애플리케이션이 이미 실행 중이지만 여기에 디버거를 연결하려고 합니다. 먼저 애플리케이션을 실행하세요:

{
    "name": "Launch file",
    "type": "go",
    "request": "launch",
    "mode": "auto",
    "program": "${file}"
}
로그인 후 복사
로그인 후 복사

프로세스 ID(PID) 확인:

taskapi/
├── go.mod
├── go.sum
├── main.go
├── task_store.go
└── task_test.go
로그인 후 복사
로그인 후 복사

다른 디버그 프로필을 추가해 보겠습니다.

import (
    "fmt"
)

type Task struct {
    ID          int    `json:"id"`
    Title       string `json:"title"`
    Description string `json:"description"`
    Done        bool   `json:"done"`
}
로그인 후 복사
로그인 후 복사

도우미 기능이 필요합니다:

type TaskStore struct {
    tasks  map[int]Task
    nextID int
}

func NewTaskStore() *TaskStore {
    return &TaskStore{
        tasks:  make(map[int]Task),
        nextID: 1,
    }
}
로그인 후 복사
로그인 후 복사

Mastering Golang Debugging in Emacs

이제 디버거를 시작합니다.

Mastering Golang Debugging in Emacs

이제 다음과 같은 POST 요청을 보내는 경우:

// CreateTask stores a given Task internally
func (ts *TaskStore) CreateTask(task Task) Task {
    task.ID = ts.nextID
    ts.tasks[task.ID] = task
    ts.nextID++
    return task
}

// GetTask retrieves a Task by ID
func (ts *TaskStore) GetTask(id int) (Task, error) {
    task, exists := ts.tasks[id]
    if !exists {
        return Task{}, fmt.Errorf("task with id %d not found", id)
    }
    return task, nil
}

// UpdateTask updates task ID with a new Task object
func (ts *TaskStore) UpdateTask(id int, task Task) error {
    if _, exists := ts.tasks[id]; !exists {
        return fmt.Errorf("task with id %d not found", id)
    }
    task.ID = id
    ts.tasks[id] = task
    return nil
}
로그인 후 복사
로그인 후 복사

디버거는 설정된 중단점에서 자동으로 중지되어야 합니다.

Mastering Golang Debugging in Emacs

Ginkgo 테스트 디버깅

Golang에서 테스트를 디버그할 수 있는 능력은 매우 중요합니다. 은행나무 테스트를 실행하기 위해 다음과 같은 여러 기능이 있는 은행나무 모드를 사용합니다.

Mastering Golang Debugging in Emacs

Mastering Golang Debugging in Emacs

그리고 결과는 다음과 같습니다.

(use-package dape
  :straight t
  :config
  ;; Pulse source line (performance hit)
  (add-hook 'dape-display-source-hook 'pulse-momentary-highlight-one-line)

  ;; To not display info and/or buffers on startup
  ;; (remove-hook 'dape-start-hook 'dape-info)
  (remove-hook 'dape-start-hook 'dape-repl))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Ginkgo용 Dape 구성

Ginkgo 테스트 디버깅을 위한 기본 구성은 다음과 같습니다.

(use-package go-mode
  :straight t
  :mode "\.go\'"
  :hook ((before-save . gofmt-before-save))
  :bind (:map go-mode-map
              ("M-?" . godoc-at-point)
              ("M-." . xref-find-definitions)
              ("M-_" . xref-find-references)
              ;; ("M-*" . pop-tag-mark) ;; Jump back after godef-jump
              ("C-c m r" . go-run))
  :custom
  (gofmt-command "goimports"))
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

go-test-ginkgo 디버그 프로필을 선택하면 테스트를 디버그할 수 있습니다.

Mastering Golang Debugging in Emacs

이제 구성은 매우 정적이므로 단위 테스트/컨테이너를 미리 선택할 수 없습니다. 어떻게든 -ginkgo.focus 매개변수를 동적으로 만들어야 합니다.

# Install Delve
go install github.com/go-delve/delve/cmd/dlv@latest

# Install gopls
go install golang.org/x/tools/gopls@latest
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

Mastering Golang Debugging in Emacs

나중에 dape-configs 변수를 살펴보면 다음 값이 표시됩니다.

go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/onsi/ginkgo/v2/ginkgo@latest

go install -v golang.org/x/tools/cmd/godoc@latest
go install -v golang.org/x/tools/cmd/goimports@latest
go install -v github.com/stamblerre/gocode@latest
go install -v golang.org/x/tools/cmd/gorename@latest
go install -v golang.org/x/tools/cmd/guru@latest
go install -v github.com/cweill/gotests/...@latest

go install -v github.com/davidrjenni/reftools/cmd/fillstruct@latest
go install -v github.com/fatih/gomodifytags@latest
go install -v github.com/godoctor/godoctor@latest
go install -v github.com/haya14busa/gopkgs/cmd/gopkgs@latest
go install -v github.com/josharian/impl@latest
go install -v github.com/rogpeppe/godef@latest
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

dape-repl 버퍼에서 디버거(디버그 중심 테스트 프로필 사용)를 시작한 후 다음을 얻습니다.

(use-package ginkgo
  :straight (:type git :host github :repo "garslo/ginkgo-mode")
  :init
  (setq ginkgo-use-pwd-as-test-dir t
        ginkgo-use-default-keys t))

(use-package gotest
  :straight t
  :after go-mode
  :bind (:map go-mode-map
              ("C-c t f" . go-test-current-file)
              ("C-c t t" . go-test-current-test)
              ("C-c t j" . go-test-current-project)
              ("C-c t b" . go-test-current-benchmark)
              ("C-c t c" . go-test-current-coverage)
              ("C-c t x" . go-run)))

(use-package go-guru
  :straight t
  :hook
  (go-mode . go-guru-hl-identifier-mode))

(use-package go-projectile
  :straight t
  :after (projectile go-mode))

(use-package flycheck-golangci-lint
  :straight t
  :hook
  (go-mode . flycheck-golangci-lint-setup))

(use-package go-eldoc
  :straight t
  :hook
  (go-mode . go-eldoc-setup))

(use-package go-tag
  :straight t
  :bind (:map go-mode-map
              ("C-c t a" . go-tag-add)
              ("C-c t r" . go-tag-remove))
  :init (setq go-tag-args (list "-transform" "camelcase")))

(use-package go-fill-struct
  :straight t)

(use-package go-impl
  :straight t)

(use-package go-playground
  :straight t)
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

?"5개 사양 중 1개"(❶)만 실행되었다는 점에 주목하세요. 이는 ginkgo가 우리가 지정한 컨테이너(❷)에만 집중했다는 의미입니다.

모범 사례 및 팁

디버깅 경험을 통해 다음과 같은 몇 가지 모범 사례를 알게 되었습니다.

  • 디버깅 구성에 버전 제어 사용
  • .dir-locals.el에서 디버그 구성을 유지합니다.
  • 구성에 의미 있는 이름을 사용하세요
  • 프로젝트별 디버깅 도우미 기능 생성
  • 로컬에서 사용자 정의(버퍼별)

리소스 및 참고 자료

  • 마스터의 vscode-go/docs/debugging.md · golang/vscode-go
  • delve/dlv dap-mode 직접 지원 · Issue #318 · emacs-lsp/dap-mode
  • Dape GitHub 저장소
  • Delve 디버거
  • Eglot 문서
  • 은행나무 테스트 프레임워크

위 내용은 Emacs에서 Golang 디버깅 마스터하기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿