Golang Go语言中的几大坑
Golang Go语言中的几大坑
可能有些不准确,别跟我说用了 N 多的 trick 怎么实现了,我说的是开箱即用
1. 接口没有默认实现, 几个数据结构实现了某个接口需要有统一的行为,每个都得写一个几乎同样的方法,太蛋疼了
2. panic 的堆栈信息,捕获是捕获了,我要把他打到日志里去??
3. 日志框架,默认能做到打印行号,打印时间,打印级别,控制按日志级别输出,对日志自动分片压缩的几乎没有
4. 蛋疼的类型信息
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 等服务捕获, 并做日志分割, 存储等功能.
- 你可以考虑一下把接口拆分得颗粒度小一点,再组合合适的功能到一起
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 试试?
楼主玩 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语言)中,确实存在一些常见的问题或“坑”,这些问题对于初学者和经验丰富的开发者都可能构成挑战。以下是一些主要的注意事项:
- 参数展开:在函数调用时,注意参数展开的问题,特别是使用切片或数组作为参数时。
- 数组值传递:数组在Go中是值传递,这意味着函数内对数组的修改不会影响到函数外的数组。
- map遍历顺序:map是基于hash表实现的,因此每次遍历的顺序都可能不同。
- recover的使用:recover必须在defer中调用,用于捕获panic异常。
- Goroutine调度:Goroutine是协作式抢占调度,避免让某个Goroutine独占CPU。
- defer执行时机:defer语句在函数退出时才会执行,注意在循环中使用defer可能导致资源延迟释放。
- 切片与内存管理:切片操作可能导致底层数组被锁定,影响内存释放。
- 内存地址变化:使用unsafe包转换内存地址时,需要注意内存地址可能会变化。
- Goroutine泄漏:不当的Goroutine管理可能导致内存泄漏,应确保Goroutine能够正确退出。
掌握这些要点有助于避免在Go语言开发中遇到常见问题,提高代码的健壮性和可维护性。