Golang Go语言中的几大坑

发布于 1周前 作者 gougou168 来自 Go语言

Golang Go语言中的几大坑
可能有些不准确,别跟我说用了 N 多的 trick 怎么实现了,我说的是开箱即用
1. 接口没有默认实现, 几个数据结构实现了某个接口需要有统一的行为,每个都得写一个几乎同样的方法,太蛋疼了
2. panic 的堆栈信息,捕获是捕获了,我要把他打到日志里去??
3. 日志框架,默认能做到打印行号,打印时间,打印级别,控制按日志级别输出,对日志自动分片压缩的几乎没有
4. 蛋疼的类型信息

49 回复

panic 的堆栈信息可以如下做
defer func() {
if r := recover(); r != nil {
logUtil.Error.Printf(“panic, %s, collecing ends”, fmt.Sprintf("%v",r))
return
}
}()

更多关于Golang Go语言中的几大坑的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


现在日志很少会靠程序自动写了, 都是程序打印到标准输出, 然后 supervisor 或 heka 等服务捕获, 并做日志分割, 存储等功能.

  1. 你可以考虑一下把接口拆分得颗粒度小一点,再组合合适的功能到一起
    2. 最好做到 no panic,做好错误检查比 defer recover 好得多

并不是每个都搞这些高大上的

对日志自动分片压缩的几乎没有
这个倒是真的 得自己实现了

1 -> 对语言了解不深,既然是 interface,自己不实现,难道用意念编程?
2 -> panic 对应的还有个叫 recover, 参考一楼的答案,学艺不精
3 -> 日志框架,你别告诉大家说你用标准库的,现在都用第三方或自己写,有个叫 logrus 的据说挺好用
4 -> 类型信息缺失挺蛋疼,还不支持 int,int32 这些类型的自动转义,这个算个槽点

楼主应该是刚入门的级别,建议多修炼修炼,吐槽的都没到点上

你应该吐槽:
1. 为毛没有泛型?
2. 从来没见过这么垃圾的包管理?
3. 什么玩意,写个项目还要定义 gopath 这个环境变量?
4. error 处理这他妈垃圾!!!
5. 为毛编译这么快,老子都没反应过来,c++不是 make 一下可以玩一把手游的么

包管理是可以吐槽

error 处理写多了,倒是没有感觉了,至少好处是每个 error 都处理,问题比以前写 Python 少多了

日志框架,默认能做到打印行号,打印时间,打印级别,控制按日志级别输出,对日志自动分片压缩的几乎没有

这个一般是第三方实现的吧 而且 go 的 log 能提供的东西已经很多了

#6 日志框架也真的是 槽点,logrus 的 issue 好几年了,行号的功能楞是没加上

感觉最近 go 代替了 php,成为了最引战语言…是不是说明 go 最近发展势头还不错?

golang 中根据首字母的大小写来确定可以访问的权限
当初没看文档,被这个坑的不要不要的。

打印行号,打印时间,打印级别,控制按日志级别输出,对日志自动分片压缩,打印调用的堆栈信息,按照执行完整流程作用域输出等等,这些功能好像并不难实现,所以自己写个就好啦,想要啥功能自己加,还能为项目需要个性化定制

学艺不精也来喷,你看 V2 多友善

map slice 默认传引用这个比较坑。

1 无解,换个语言可破;
2 是用法你没了解;
3 这个库有的,你再找找?不过这样程序会慢,个人建议性能敏感程序不要打印文件行号信息;
4. 换个 Rust 试试?

我想吐槽的是 p1 *P =&P{}和 p2 *P=P{}。。居然都尼玛可以。。。还有接受指针压栈的函数。。在写代码的时候 p3 P =P{}。。p3 也能直接调用。。。
综上 。。所写非所得。。。 每次写代码前都要先想下 p 到底是不是指针。。。也许是我写的太少的原因。。。
其他的泛型。。。error 处理方式。。包管理。。。还没有 ThreadLocalStorage。。其他什么的就不 BB 了。。。

楼主玩 go 一两天的经验。。。

我想要 ThreadContext 啊,一个 ctx 显示传来传去非常蛋疼。

没 get 到第一个问题是什么问题,编译不过的吧?能否给个代码看看?另外那个真的不是传统意义上的“指针”…

打印行号: 默认有

打印时间 : 默认有

打印级别,控制按日志级别输出: 关于这个非常同意 Dave cheney 的观点,完全没必要分很细的日志界别 [链接]!( https://dave.cheney.net/2015/11/05/lets-talk-about-logging)

日志自动分片压缩: logrotate 写个几行配置,然后程序里响应一下 SIGHUP 信号,重新开一下日志文件,搞定。

自己写一个是一方面,我现在确实是改的 logrus,但是有一个好的库,还是会更好

我觉得根据日志级别控制输出是很常用的功能。。。。
还有 logrotate 这个,真心在 windows 上就不好搞了

我都是跟这感觉写的,都不管指针不指针的

你看懂了么,interface 的默认实现??
就是说几个
type struct1 struct{…}
type struct2 struct{…}
都实现了某个接口,这个接口其实对于大家都是一样的,如果有个默认实现就好了,不用每个 struct 都去写同样的方法,除了 receiver 不一样之外

我玩了几天也这就遇到了这些问题,实在惭愧

Golang 新手签到,刚刚遇到一个小坑。。。时间格式化的 Fomat、Parse 方法的格式参数。。。。竟然是那种写死的字符串。。。真是写的时候就透露着一股尴尬。。。

golang<br> datetime := time.Now().Format("2006-01-02 15:04:05") //后面的参数是固定的 否则将无法正常输出<br>

我也是这样想的,感觉 Go 社区走的路子怪怪的

如果是这样你试试 embed struct ?

你这个完全是 go 好心办坏事,go 怕一些不懂或者不完全懂指针的用户指针的用法不对,造成一些他们意向不到的问题,所以将你说的这些情况都给兼容了。如果是玩过 C/C++的,根本不会有这种疑问,指针和形参还是很好区分的,另建议使用指针操作。
go 里面玩指针就按照 C/C++那套整就完全没问题,注意一下 map, slice 默认是引用传递( go 官方不承认用“引用传递”这个说法)

对于操作时间,给你个建议,任何时候都带上时区去计算,否则对时间强依赖的项目,死都不知道怎么死的,运维给你服务器整个 UTC 时区,你按照北京时间算

log 确实没有像 java log4j 那种功能齐备的库
不过我自己用的时候标准库的 log 上自定义部分功能+logrotate 基本满足需求了

是个办法,谢谢,

所以说你是第一天玩呢!!
你在 struct1 和 struct2 上都”继承“一个基类 struct3,然后让 struct3 去实现你想要的同一样且不需要写两次的接口不就行了么?

想玩面向对象编程那套,go 并不是不是可以,只不过现有的基础设施比较简陋,实现的面向对象不能像 C++和 Java 那么简便和高大上罢了

多看看一些开源项目对深入提升比较有好处

感谢提醒,我看下



光要控制日志输出,很容易, 错误信息 info 输出,调试信息 debug 输出,加一个控制开关关闭 debug 输出
分那么多 info,warn,error,fatal 就是脱裤子放*, 你真的会用那么多级别来控制程序的日志输出?
交给运维人员,运维人员会用你那么多的参数设置?

windows 这个真不懂了,没有在 windows 上跑服务程序的经验。

哈哈我也来说一个坑:
结构体是值类型,切片是引用类型
我把一个结构体传入函数(不传指针),函数里修改结构体内容(比如 append 结构体替换原有结构体只全局可见)。如果直接修改结构体里的切片则是全局可见

debug, info, warning, error 这四个级别还是要的,debug 是调试的时候打印信息,info 是线上一些做事情的日志,比如定时器啊之类的,这样查问题可以看到有没有做,warning 就是一些不是很重要的错误,error 就是重要的错误


append 结构体替换原有结构体只局部可见

自己写个辅助函数转一下即可
go<br><br>// Format time.Time struct to string<br>// MM - month - 01<br>// M - month - 1, single bit<br>// DD - day - 02<br>// D - day 2<br>// YYYY - year - 2006<br>// YY - year - 06<br>// HH - 24 hours - 03<br>// H - 24 hours - 3<br>// hh - 12 hours - 03<br>// h - 12 hours - 3<br>// mm - minute - 04<br>// m - minute - 4<br>// ss - second - 05<br>// s - second = 5<br>func FormatDate(format string, t ...time.Time) string {<br> var datetime time.Time<br> if len(t) == 0 {<br> datetime = time.Now()<br> } else {<br> datetime = t[0]<br> }<br><br> res := strings.Replace(format, "MM", datetime.Format("01"), -1)<br> res = strings.Replace(res, "M", datetime.Format("1"), -1)<br> res = strings.Replace(res, "DD", datetime.Format("02"), -1)<br> res = strings.Replace(res, "D", datetime.Format("2"), -1)<br> res = strings.Replace(res, "YYYY", datetime.Format("2006"), -1)<br> res = strings.Replace(res, "YY", datetime.Format("06"), -1)<br> res = strings.Replace(res, "HH", fmt.Sprintf("%02d", datetime.Hour()), -1)<br> res = strings.Replace(res, "H", fmt.Sprintf("%d", datetime.Hour()), -1)<br> res = strings.Replace(res, "hh", datetime.Format("03"), -1)<br> res = strings.Replace(res, "h", datetime.Format("3"), -1)<br> res = strings.Replace(res, "mm", datetime.Format("04"), -1)<br> res = strings.Replace(res, "m", datetime.Format("4"), -1)<br> res = strings.Replace(res, "ss", datetime.Format("05"), -1)<br> res = strings.Replace(res, "s", datetime.Format("5"), -1)<br> return res<br>}

日志是你没用对库吧。std 里日志没有那么完整的支持,这种一般都不会在 std 里,c++, java 也是这样。

为什么要把 panic 错误打印到日志?这说明程序写的并不够健壮,panic 错误是要爆出来给人看的,不然程序多少暗坑都不知道

1, embedded field
2, runtime.Callers
3, 自己写
4, 蛋疼可能是肾亏

#6
是 interface 就得自己实现没有什么理论依据啊,连 Java 的 interface 都能放 default 了,Go 以后也不一定不加

并非引用传递,看源码就知道了,是值传递了指针成员变量。

我也说一个…遍历一个结构体 slice 然后在循环内部取遍历出来的结构体地址…发现都是同一个地址

包管理,泛型,gopath 三大坑!谁用税知道!

感觉最大的坑就是包管理
现在 go mod 稍有改善的迹象

这个真的是很尴尬啊。

在Golang(Go语言)中,确实存在一些常见的问题或“坑”,这些问题对于初学者和经验丰富的开发者都可能构成挑战。以下是一些主要的注意事项:

  1. 参数展开:在函数调用时,注意参数展开的问题,特别是使用切片或数组作为参数时。
  2. 数组值传递:数组在Go中是值传递,这意味着函数内对数组的修改不会影响到函数外的数组。
  3. map遍历顺序:map是基于hash表实现的,因此每次遍历的顺序都可能不同。
  4. recover的使用:recover必须在defer中调用,用于捕获panic异常。
  5. Goroutine调度:Goroutine是协作式抢占调度,避免让某个Goroutine独占CPU。
  6. defer执行时机:defer语句在函数退出时才会执行,注意在循环中使用defer可能导致资源延迟释放。
  7. 切片与内存管理:切片操作可能导致底层数组被锁定,影响内存释放。
  8. 内存地址变化:使用unsafe包转换内存地址时,需要注意内存地址可能会变化。
  9. Goroutine泄漏:不当的Goroutine管理可能导致内存泄漏,应确保Goroutine能够正确退出。

掌握这些要点有助于避免在Go语言开发中遇到常见问题,提高代码的健壮性和可维护性。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!