成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何在Go的函數中得到調用者函數名?

開發 前端
有時候在Go的函數調用的過程中,我們需要知道函數被誰調用,比如打印日志信息等。例如下面的函數,我們希望在日志中打印出調用者的名字。

有時候在Go的函數調用的過程中,我們需要知道函數被誰調用,比如打印日志信息等。例如下面的函數,我們希望在日志中打印出調用者的名字。

如何在Go的函數中得到調用者函數名?

 

  1. func Foo() { 
  2.     fmt.Println("誰在調用我?"
  3.     bar() 
  4. func Bar() { 
  5.     fmt.Println("誰又在調用我?"

首先打印函數本身的名稱

最簡單的方式就是硬編碼。 因為在編譯之前,我們肯定知道打印的時候所在哪個函數,但是更好的方式是編寫一個通用的函數,比如下面的例子:

 

  1. package main 
  2. import ( 
  3.     "fmt" 
  4.     "runtime" 
  5. func main() { 
  6.     Foo() 
  7. func Foo() { 
  8.     fmt.Printf("我是 %s, 誰在調用我?\n", printMyName()) 
  9.     Bar() 
  10. func Bar() { 
  11.     fmt.Printf("我是 %s, 誰又在調用我?\n", printMyName()) 
  12. func printMyName() string { 
  13.     pc, _, _, _ := runtime.Caller(1) 
  14.     return runtime.FuncForPC(pc).Name() 

輸出結果:

  1. 我是 main.Foo, 誰在調用我? 
  2. 我是 main.Bar, 誰又在調用我? 

可以看到函數在被調用的時候,printMyName把函數本身的名字打印出來了,注意這里Caller的參數是1, 因為我們將業務代碼封裝成了一個函數。

首先打印函數調用者的名稱

將上面的代碼修改一下,增加一個新的printCallerName的函數,可以打印調用者的名稱。

 

  1. func main() { 
  2.     Foo() 
  3. func Foo() { 
  4.     fmt.Printf("我是 %s, %s 在調用我!\n", printMyName(), printCallerName()) 
  5.     Bar() 
  6. func Bar() { 
  7.     fmt.Printf("我是 %s, %s 又在調用我!\n", printMyName(), printCallerName()) 
  8. func printMyName() string { 
  9.     pc, _, _, _ := runtime.Caller(1) 
  10.     return runtime.FuncForPC(pc).Name() 
  11. func printCallerName() string { 
  12.     pc, _, _, _ := runtime.Caller(2) 
  13.     return runtime.FuncForPC(pc).Name() 

相關函數介紹

你可以通過runtime.Caller、runtime.Callers、runtime.FuncForPC等函數更詳細的跟蹤函數的調用堆棧。

1、func Caller(skip int) (pc uintptr, file string, line int, ok bool)

Caller可以返回函數調用棧的某一層的程序計數器、文件信息、行號。

0 代表當前函數,也是調用runtime.Caller的函數。1 代表上一層調用者,以此類推。

2、func Callers(skip int, pc []uintptr) int

Callers用來返回調用站的程序計數器, 放到一個uintptr中。

0 代表 Callers 本身,這和上面的Caller的參數的意義不一樣,歷史原因造成的。 1 才對應這上面的 0。

比如在上面的例子中增加一個trace函數,被函數Bar調用。

 

  1. …… 
  2. func Bar() { 
  3.     fmt.Printf("我是 %s, %s 又在調用我!\n", printMyName(), printCallerName()) 
  4.     trace() 
  5. func trace() { 
  6.     pc := make([]uintptr, 10) // at least 1 entry needed 
  7.     n := runtime.Callers(0, pc) 
  8.     for i := 0; i < n; i++ { 
  9.         f := runtime.FuncForPC(pc[i]) 
  10.         file, line := f.FileLine(pc[i]) 
  11.         fmt.Printf("%s:%d %s\n", file, line, f.Name()) 
  12.     } 

輸出結果可以看到這個goroutine的整個棧都打印出來了:

 

  1. /usr/local/go/src/runtime/extern.go:218 runtime.Callers 
  2. /Users/yuepan/go/src/git.intra.weibo.com/platform/tool/g/main.go:34 main.trace 
  3. /Users/yuepan/go/src/git.intra.weibo.com/platform/tool/g/main.go:20 main.Bar 
  4. /Users/yuepan/go/src/git.intra.weibo.com/platform/tool/g/main.go:15 main.Foo 
  5. /Users/yuepan/go/src/git.intra.weibo.com/platform/tool/g/main.go:10 main.main 
  6. /usr/local/go/src/runtime/proc.go:210 runtime.main 
  7. /usr/local/go/src/runtime/asm_amd64.s:1334 runtime.goexit 

3、func CallersFrames(callers []uintptr) *Frames

上面的Callers只是或者棧的程序計數器,如果想獲得整個棧的信息,可以使用CallersFrames函數,省去遍歷調用FuncForPC。

上面的trace函數可以更改為下面的方式:

 

  1. func trace2() { 
  2.     pc := make([]uintptr, 10) // at least 1 entry needed 
  3.     n := runtime.Callers(0, pc) 
  4.     frames := runtime.CallersFrames(pc[:n]) 
  5.     for { 
  6.         frame, more := frames.Next() 
  7.         fmt.Printf("%s:%d %s\n", frame.File, frame.Line, frame.Function
  8.         if !more { 
  9.             break 
  10.         } 
  11.     } 

4、func FuncForPC(pc uintptr) *Func

FuncForPC 是一個有趣的函數, 它可以把程序計數器地址對應的函數的信息獲取出來。如果因為內聯程序計數器對應多個函數,它返回最外面的函數。

它的返回值是一個*Func類型的值,通過*Func可以獲得函數地址、文件行、函數名等信息。

除了上面獲取程序計數器的方式,也可以通過反射的方式獲取函數的地址:

  1. runtime.FuncForPC(reflect.ValueOf(foo).Pointer()).Name() 

5、獲取程序堆棧

在程序panic的時候,一般會自動把堆棧打出來,如果你想在程序中獲取堆棧信息,可以通過debug.PrintStack()打印出來。比如你在程序中遇到一個Error,但是不期望程序panic,只是想把堆棧信息打印出來以便跟蹤調試,你可以使用debug.PrintStack()。

抑或,你自己讀取堆棧信息,自己處理和打印:

 

  1. func DumpStacks() { 
  2.     buf := make([]byte, 16384) 
  3.     buf = buf[:runtime.Stack(buf, true)] 
  4.     fmt.Printf("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf) 

參考 調試利器:dump goroutine 的 stacktrace。

利用堆棧信息還可以獲取goroutine的id, 參考: 再談談獲取 goroutine id 的方法

 

  1. func GoID() int { 
  2.     var buf [64]byte 
  3.     n := runtime.Stack(buf[:], false
  4.     idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] 
  5.     id, err := strconv.Atoi(idField) 
  6.     if err != nil { 
  7.         panic(fmt.Sprintf("cannot get goroutine id: %v", err)) 
  8.     } 
  9.     return id 

 

責任編輯:未麗燕 來源: 鳥窩
相關推薦

2022-05-05 09:02:24

Go函數調用棧

2020-06-17 17:29:11

BashLinux

2021-09-15 07:56:33

函數類型Go

2021-11-28 06:47:32

Python函數開發

2021-11-02 12:19:18

Go函數結構

2021-07-09 12:37:31

GoPython編程語言

2010-07-28 15:29:18

Flex函數

2010-01-28 13:35:41

調用C++函數

2017-07-27 15:52:10

函數調用堆棧結構

2023-10-23 19:27:21

Go函數

2009-07-10 17:54:15

Java中調用JythJython

2010-09-10 16:02:13

SQLCHARINDEX函數

2021-12-27 08:53:23

Go函數 Nil

2018-08-27 14:50:46

LinuxShellBash

2019-12-02 21:29:45

Keras神經網絡TensorFlow

2022-01-14 15:05:56

函數調用代碼Linux

2009-06-17 13:19:50

Java調用DLL

2022-09-19 11:42:21

Go優化CPU

2020-05-06 20:40:03

Go編程語言

2021-04-13 07:58:42

Go語言函數
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品68久久久一区 | 午夜精品导航 | 黄色毛片在线观看 | 日本久久久久久 | 欧美精三区欧美精三区 | 99热.com| 成人小视频在线观看 | 成人美女免费网站视频 | 99小视频 | 亚洲欧美一区二区三区情侣bbw | 国产精品久久久久久久久久免费看 | 成人在线小视频 | 人人天天操 | 中文字幕第十页 | 欧美aaaaaaaaaa| 在线免费观看a级片 | 国产精品美女久久久 | 久久成人精品视频 | 欧美视频精品 | 欧美视频免费在线观看 | 国产国产精品 | 亚洲伊人精品酒店 | 免费三级黄 | 中文字幕亚洲视频 | 亚洲一区二区久久 | 九九热最新地址 | 中文字幕在线精品 | 啪一啪| 天天操 夜夜操 | 国产高清精品一区二区三区 | 国产羞羞视频在线观看 | h在线播放 | 99精品视频一区二区三区 | 国产伦精品一区二区三区高清 | 老熟女毛片 | 给我免费的视频在线观看 | 7799精品视频天天看 | 自拍 亚洲 欧美 老师 丝袜 | 欧美亚洲第一区 | 成人一区二区三区在线观看 | 欧美成人a∨高清免费观看 欧美日韩中 |