V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
hijoker
V2EX  ›  Go 编程语言

Go 的几大坑

  •  1
     
  •   hijoker · 2018-09-30 14:19:21 +08:00 · 6294 次点击
    这是一个创建于 2278 天前的主题,其中的信息可能已经有所发展或是发生改变。
    可能有些不准确,别跟我说用了 N 多的 trick 怎么实现了,我说的是开箱即用
    1. 接口没有默认实现, 几个数据结构实现了某个接口需要有统一的行为,每个都得写一个几乎同样的方法,太蛋疼了
    2. panic 的堆栈信息,捕获是捕获了,我要把他打到日志里去??
    3. 日志框架,默认能做到打印行号,打印时间,打印级别,控制按日志级别输出,对日志自动分片压缩的几乎没有
    4. 蛋疼的类型信息
    48 条回复    2018-10-01 18:30:21 +08:00
    hijoker
        1
    hijoker  
    OP
       2018-09-30 14:44:13 +08:00
    panic 的堆栈信息可以如下做
    defer func() {
    if r := recover(); r != nil {
    logUtil.Error.Printf("panic, %s, collecing ends", fmt.Sprintf("%v",r))
    return
    }
    }()
    Mohanson
        2
    Mohanson  
       2018-09-30 14:52:19 +08:00
    现在日志很少会靠程序自动写了, 都是程序打印到标准输出, 然后 supervisor 或 heka 等服务捕获, 并做日志分割, 存储等功能.
    heimeil
        3
    heimeil  
       2018-09-30 15:03:19 +08:00
    1. 你可以考虑一下把接口拆分得颗粒度小一点,再组合合适的功能到一起
    2. 最好做到 no panic,做好错误检查比 defer recover 好得多
    hijoker
        4
    hijoker  
    OP
       2018-09-30 15:48:56 +08:00
    @Mohanson 并不是每个都搞这些高大上的
    Immortal
        5
    Immortal  
       2018-09-30 15:59:34 +08:00
    对日志自动分片压缩的几乎没有
    这个倒是真的 得自己实现了
    xkeyideal
        6
    xkeyideal  
       2018-09-30 16:04:57 +08:00   ❤️ 12
    1 -> 对语言了解不深,既然是 interface,自己不实现,难道用意念编程?
    2 -> panic 对应的还有个叫 recover, 参考一楼的答案,学艺不精
    3 -> 日志框架,你别告诉大家说你用标准库的,现在都用第三方或自己写,有个叫 logrus 的据说挺好用
    4 -> 类型信息缺失挺蛋疼,还不支持 int,int32 这些类型的自动转义,这个算个槽点

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

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

    error 处理写多了,倒是没有感觉了,至少好处是每个 error 都处理,问题比以前写 Python 少多了
    tourist2018
        8
    tourist2018  
       2018-09-30 16:17:07 +08:00
    日志框架,默认能做到打印行号,打印时间,打印级别,控制按日志级别输出,对日志自动分片压缩的几乎没有

    这个一般是第三方实现的吧 而且 go 的 log 能提供的东西已经很多了
    zwpaper
        9
    zwpaper  
       2018-09-30 16:21:05 +08:00
    @xkeyideal #6 日志框架也真的是 槽点,logrus 的 issue 好几年了,行号的功能楞是没加上
    feiyuanqiu
        10
    feiyuanqiu  
       2018-09-30 16:22:00 +08:00
    感觉最近 go 代替了 php,成为了最引战语言...是不是说明 go 最近发展势头还不错?
    hcymk2
        11
    hcymk2  
       2018-09-30 16:23:43 +08:00
    golang 中根据首字母的大小写来确定可以访问的权限
    当初没看文档,被这个坑的不要不要的。
    xkeyideal
        12
    xkeyideal  
       2018-09-30 16:32:22 +08:00
    @zwpaper 打印行号,打印时间,打印级别,控制按日志级别输出,对日志自动分片压缩,打印调用的堆栈信息,按照执行完整流程作用域输出等等,这些功能好像并不难实现,所以自己写个就好啦,想要啥功能自己加,还能为项目需要个性化定制
    lideshun123
        13
    lideshun123  
       2018-09-30 16:35:31 +08:00
    学艺不精也来喷,你看 V2 多友善
    zarte
        14
    zarte  
       2018-09-30 16:58:47 +08:00
    map slice 默认传引用这个比较坑。
    janxin
        15
    janxin  
       2018-09-30 17:00:34 +08:00
    1 无解,换个语言可破;
    2 是用法你没了解;
    3 这个库有的,你再找找?不过这样程序会慢,个人建议性能敏感程序不要打印文件行号信息;
    4. 换个 Rust 试试?
    Aruforce
        16
    Aruforce  
       2018-09-30 17:01:44 +08:00   ❤️ 1
    我想吐槽的是 p1 *P =&P{}和 p2 *P=P{}。。居然都尼玛可以。。。还有接受指针压栈的函数。。在写代码的时候 p3 P =P{}。。p3 也能直接调用。。。
    综上 。。所写非所得。。。 每次写代码前都要先想下 p 到底是不是指针。。。也许是我写的太少的原因。。。
    其他的泛型。。。error 处理方式。。包管理。。。还没有 ThreadLocalStorage。。其他什么的就不 BB 了。。。
    jitongxi
        17
    jitongxi  
       2018-09-30 17:04:16 +08:00
    楼主玩 go 一两天的经验。。。
    janxin
        18
    janxin  
       2018-09-30 17:07:08 +08:00
    @Aruforce 我想要 ThreadContext 啊,一个 ctx 显示传来传去非常蛋疼。

    没 get 到第一个问题是什么问题,编译不过的吧?能否给个代码看看?另外那个真的不是传统意义上的“指针”...
    darrh00
        19
    darrh00  
       2018-09-30 17:08:50 +08:00
    打印行号: 默认有

    打印时间 : 默认有

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

    日志自动分片压缩: logrotate 写个几行配置,然后程序里响应一下 SIGHUP 信号,重新开一下日志文件,搞定。
    zwpaper
        20
    zwpaper  
       2018-09-30 17:15:08 +08:00 via iPhone
    @xkeyideal 自己写一个是一方面,我现在确实是改的 logrus,但是有一个好的库,还是会更好
    hijoker
        21
    hijoker  
    OP
       2018-09-30 17:15:58 +08:00
    @darrh00 我觉得根据日志级别控制输出是很常用的功能。。。。
    还有 logrotate 这个,真心在 windows 上就不好搞了
    aliipay
        22
    aliipay  
       2018-09-30 17:16:49 +08:00
    @Aruforce 我都是跟这感觉写的,都不管指针不指针的
    hijoker
        23
    hijoker  
    OP
       2018-09-30 17:18:50 +08:00
    @xkeyideal 你看懂了么,interface 的默认实现??
    就是说几个
    type struct1 struct{...}
    type struct2 struct{...}
    都实现了某个接口,这个接口其实对于大家都是一样的,如果有个默认实现就好了,不用每个 struct 都去写同样的方法,除了 receiver 不一样之外
    hijoker
        24
    hijoker  
    OP
       2018-09-30 17:19:17 +08:00
    @lideshun123 请指教
    hijoker
        25
    hijoker  
    OP
       2018-09-30 17:19:49 +08:00
    @jitongxi 我玩了几天也这就遇到了这些问题,实在惭愧
    silov
        26
    silov  
       2018-09-30 17:20:54 +08:00
    Golang 新手签到,刚刚遇到一个小坑。。。时间格式化的 Fomat、Parse 方法的格式参数。。。。竟然是那种写死的字符串。。。真是写的时候就透露着一股尴尬。。。

    ```golang
    datetime := time.Now().Format("2006-01-02 15:04:05") //后面的参数是固定的 否则将无法正常输出
    ```
    hijoker
        27
    hijoker  
    OP
       2018-09-30 17:21:06 +08:00
    @zwpaper 我也是这样想的,感觉 Go 社区走的路子怪怪的
    janxin
        28
    janxin  
       2018-09-30 17:22:09 +08:00
    @hijoker 如果是这样你试试 embed struct ?
    xkeyideal
        29
    xkeyideal  
       2018-09-30 17:22:24 +08:00
    @Aruforce 你这个完全是 go 好心办坏事,go 怕一些不懂或者不完全懂指针的用户指针的用法不对,造成一些他们意向不到的问题,所以将你说的这些情况都给兼容了。如果是玩过 C/C++的,根本不会有这种疑问,指针和形参还是很好区分的,另建议使用指针操作。
    go 里面玩指针就按照 C/C++那套整就完全没问题,注意一下 map, slice 默认是引用传递( go 官方不承认用“引用传递”这个说法)
    xkeyideal
        30
    xkeyideal  
       2018-09-30 17:24:32 +08:00   ❤️ 2
    @silov 对于操作时间,给你个建议,任何时候都带上时区去计算,否则对时间强依赖的项目,死都不知道怎么死的,运维给你服务器整个 UTC 时区,你按照北京时间算
    lwldcr
        31
    lwldcr  
       2018-09-30 17:25:06 +08:00
    log 确实没有像 java log4j 那种功能齐备的库
    不过我自己用的时候标准库的 log 上自定义部分功能+logrotate 基本满足需求了
    hijoker
        32
    hijoker  
    OP
       2018-09-30 17:26:04 +08:00
    @janxin 是个办法,谢谢,
    xkeyideal
        33
    xkeyideal  
       2018-09-30 17:28:40 +08:00
    @hijoker 所以说你是第一天玩呢!!
    你在 struct1 和 struct2 上都”继承“一个基类 struct3,然后让 struct3 去实现你想要的同一样且不需要写两次的接口不就行了么?

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

    多看看一些开源项目对深入提升比较有好处
    silov
        34
    silov  
       2018-09-30 17:31:21 +08:00
    @xkeyideal 感谢提醒,我看下
    darrh00
        35
    darrh00  
       2018-09-30 17:40:11 +08:00
    @hijoker

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

    windows 这个真不懂了,没有在 windows 上跑服务程序的经验。
    whoami9894
        36
    whoami9894  
       2018-09-30 17:47:21 +08:00 via Android   ❤️ 1
    哈哈我也来说一个坑:
    结构体是值类型,切片是引用类型
    我把一个结构体传入函数(不传指针),函数里修改结构体内容(比如 append 结构体替换原有结构体只全局可见)。如果直接修改结构体里的切片则是全局可见
    RaynorGu
        37
    RaynorGu  
       2018-09-30 17:49:07 +08:00
    @darrh00 debug, info, warning, error 这四个级别还是要的,debug 是调试的时候打印信息,info 是线上一些做事情的日志,比如定时器啊之类的,这样查问题可以看到有没有做,warning 就是一些不是很重要的错误,error 就是重要的错误
    whoami9894
        38
    whoami9894  
       2018-09-30 17:50:21 +08:00 via Android
    @whoami9894
    append 结构体替换原有结构体只局部可见
    jinzhe
        39
    jinzhe  
       2018-09-30 18:06:03 +08:00
    @silov 自己写个辅助函数转一下即可
    ```go

    // Format time.Time struct to string
    // MM - month - 01
    // M - month - 1, single bit
    // DD - day - 02
    // D - day 2
    // YYYY - year - 2006
    // YY - year - 06
    // HH - 24 hours - 03
    // H - 24 hours - 3
    // hh - 12 hours - 03
    // h - 12 hours - 3
    // mm - minute - 04
    // m - minute - 4
    // ss - second - 05
    // s - second = 5
    func FormatDate(format string, t ...time.Time) string {
    var datetime time.Time
    if len(t) == 0 {
    datetime = time.Now()
    } else {
    datetime = t[0]
    }

    res := strings.Replace(format, "MM", datetime.Format("01"), -1)
    res = strings.Replace(res, "M", datetime.Format("1"), -1)
    res = strings.Replace(res, "DD", datetime.Format("02"), -1)
    res = strings.Replace(res, "D", datetime.Format("2"), -1)
    res = strings.Replace(res, "YYYY", datetime.Format("2006"), -1)
    res = strings.Replace(res, "YY", datetime.Format("06"), -1)
    res = strings.Replace(res, "HH", fmt.Sprintf("%02d", datetime.Hour()), -1)
    res = strings.Replace(res, "H", fmt.Sprintf("%d", datetime.Hour()), -1)
    res = strings.Replace(res, "hh", datetime.Format("03"), -1)
    res = strings.Replace(res, "h", datetime.Format("3"), -1)
    res = strings.Replace(res, "mm", datetime.Format("04"), -1)
    res = strings.Replace(res, "m", datetime.Format("4"), -1)
    res = strings.Replace(res, "ss", datetime.Format("05"), -1)
    res = strings.Replace(res, "s", datetime.Format("5"), -1)
    return res
    }```
    bobuick
        40
    bobuick  
       2018-09-30 18:26:40 +08:00
    日志是你没用对库吧。std 里日志没有那么完整的支持,这种一般都不会在 std 里,c++, java 也是这样。
    Leigg
        41
    Leigg  
       2018-09-30 19:24:56 +08:00 via iPhone
    为什么要把 panic 错误打印到日志?这说明程序写的并不够健壮,panic 错误是要爆出来给人看的,不然程序多少暗坑都不知道
    reus
        42
    reus  
       2018-09-30 20:49:17 +08:00   ❤️ 1
    1, embedded field
    2, runtime.Callers
    3, 自己写
    4, 蛋疼可能是肾亏
    icris
        43
    icris  
       2018-09-30 22:41:13 +08:00
    @xkeyideal #6
    是 interface 就得自己实现没有什么理论依据啊,连 Java 的 interface 都能放 default 了,Go 以后也不一定不加
    xeaglex
        44
    xeaglex  
       2018-10-01 01:06:41 +08:00
    @xkeyideal 并非引用传递,看源码就知道了,是值传递了指针成员变量。
    blless
        45
    blless  
       2018-10-01 09:14:45 +08:00 via Android
    我也说一个…遍历一个结构体 slice 然后在循环内部取遍历出来的结构体地址…发现都是同一个地址
    sxw11
        46
    sxw11  
       2018-10-01 09:36:59 +08:00 via Android
    包管理,泛型,gopath 三大坑!谁用税知道!
    mosliu
        47
    mosliu  
       2018-10-01 10:27:58 +08:00
    感觉最大的坑就是包管理
    现在 go mod 稍有改善的迹象
    wenzhoou
        48
    wenzhoou  
       2018-10-01 18:30:21 +08:00 via Android
    @silov 这个真的是很尴尬啊。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5741 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 03:06 · PVG 11:06 · LAX 19:06 · JFK 22:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.