十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Grafana Tempo 中的 go-humanize 工具库:让字节、时间与数字输出更人性化

Grafana Tempo 中的 go-humanize 工具库:让字节、时间与数字输出更人性化 Grafana Tempo 中的 go-humanize 工具库让字节、时间与数字输出更人性化【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo导读github.com/dustin/go-humanize是一个专注于把机器友好的原始数值转换为人类可读字符串的 Go 工具库把82854982变成83 MB把time.Time变成7 hours ago把1000000变成1,000,000。Grafana Tempo 在 go.mod 中将其以 v1.0.1 版本引入并以 vendor 方式固化在仓库中见 vendor/github.com/dustin/go-humanize用于在日志、测试与输出场景中把内部数值翻译成直观表达。读完本文你将掌握该库全部核心 API 的用法、输出规则与底层实现原理并能在自己的 Go 项目中直接复用这些能力。一、库概览一行go get就能引入的纯标准库工具go-humanize 的定位非常轻量整个核心包只依赖 Go 标准库fmt、strconv、math、sort、time、regexp等没有任何外部运行时依赖符合 Tempo minimal dependency 的设计取向。引入方式在 README.markdown 中说明import github.com/dustin/go-humanize使用时以humanize作为包名即可。它提供的功能按主题可分为六大类Sizes字节数的人性化展示与反向解析Times相对时间表达3 天前Ordinals序数词1st / 2nd / 3rdCommas千分位分组Ftoa去除尾随零的浮点格式化SI notation国际单位制前缀k/M/G/n 等。另有humanize/english子包处理英文复数与词列连接。全部函数都在包的 humanize.go 与各主题源码文件中给出实现下文逐一展开。二、Sizes字节数的双轨制格式化SI 与 IEC这是整个库中使用频率最高的功能。核心目标是把类似82854982的裸字节数转换成用户一眼能读懂的83 MB或79 MiB。2.1 两个入口Bytes与IBytesfmt.Printf(That file is %s., humanize.Bytes(82854982)) // That file is 83 MB.Bytes(s uint64)使用SI 单位制以 1000 为底后缀序列为B / kB / MB / GB / TB / PB / EBIBytes(s uint64)使用IEC 单位制二进制倍数以 1024 为底后缀序列为B / KiB / MiB / GiB / TiB / PiB / EiB。两者共享同一个底层算法humanateBytes见 bytes.gofunc humanateBytes(s uint64, base float64, sizes []string) string { if s 10 { return fmt.Sprintf(%d B, s) } e : math.Floor(logn(float64(s), base)) suffix : sizes[int(e)] val : math.Floor(float64(s)/math.Pow(base, e)*100.5) / 10 f : %.0f %s if val 10 { f %.1f %s } return fmt.Sprintf(f, val, suffix) }实现要点指数e通过对数log_base(s)取整得到从而确定落在哪一级后缀结果值保留1 位小数四舍五入到十分位当换算后的数值 ≥ 10 时退化为整数展示小于 10 字节的数字直接原样输出不做任何换算。2.2 单位常量与反向解析ParseBytes源码中定义了两套单位常量bytes.goIECByte 1 (iota * 10)依次得到KiByte / MiByte / GiByte / TiByte / PiByte / EiByte每级 ×1024SIKByte IByte * 1000依次得到MByte / GByte / TByte / PByte / EByte每级 ×1000。与正向格式化对应ParseBytes能把字符串解析回字节数bytes.go支持数字部分含.与,逗号会被剥离单位部分不区分大小写并支持无后缀的简写形式如k、m、ki、mi。示例humanize.ParseBytes(42 MB) // - 42000000, nil humanize.ParseBytes(42 mib) // - 44040192, nil若后缀无法识别不在bytesSizeTable中会返回unhandled size name: ...错误若换算结果超过math.MaxUint64会返回too large错误。2.3 大数据量版本bigbytes.go对于超出uint64范围的超大数值同目录下的 bigbytes.go 提供了基于math/big的BigBytes/BigIBytes/ParseBigBytes思路与上面一致只是底层换成大整数运算。三、Times把time.Time变成多久之前 / 多久之后3.1 最简用法Timefmt.Printf(This was touched %s., humanize.Time(someTimeInstance)) // This was touched 7 hours ago.Time(then time.Time)内部等价于RelTime(then, time.Now(), ago, from now)见 times.go过去的时间点后缀ago未来的时间点后缀from now。3.2 相对时间算法RelTime与CustomRelTimeRelTime(a, b, albl, blbl)比较两个时间点时间在前者使用albl标签在后者使用blbl标签CustomRelTime(a, b, albl, blbl, magnitudes)允许传入自定义的分档表。核心算法times.go计算diff b.Sub(a)若a晚于b则交换方向并切换标签用sort.Search在分档表magnitudes中二分查找第一个D diff的档位按该档位的Format模板填充%s替换为方向标签%d替换为diff / DivBy。3.3 默认分档表可直接参照的展示规则默认档位定义在 times.go这里完整列出时间差范围输出格式换算除数 1 秒now— 2 秒1 second %s— 1 分钟%d seconds %s秒 2 分钟1 minute %s— 1 小时%d minutes %s分钟 2 小时1 hour %s— 1 天%d hours %s小时 2 天1 day %s— 1 周%d days %s天 2 周1 week %s— 1 月30 天%d weeks %s周 2 月1 month %s— 1 年%d months %s月 18 月1 year %s— 2 年2 years %s— 37 年%d years %s年更长a long while %s—其中Day 24h、Week 7 * Day、Month 30 * Day、Year 12 * Month、LongTime 37 * Year都在 times.go 中定义。可以看出该库对1 个单位如 1 second、1 minute、1 day使用单数固定模板大于 1 个单位的差值才进入多单位 数字模板这正是它输出自然度的来源。四、Ordinals序数词输出序数功能源自 Go 邮件列表的一次讨论用于把整数变成序数表达0 - 0th 1 - 1st 2 - 2nd 3 - 3rd 4 - 4th用法README.markdownfmt.Printf(Youre my %s best friend., humanize.Ordinal(193)) // You are my 193rd best friend.实现ordinals.go只依赖strconv规则符合英语习惯默认后缀th仅当个位数为 1/2/3 且不是11/12/13即x%100不为 11/12/13时才改用st/nd/rd。五、Commas千分位分组5.1Commaint64fmt.Printf(You owe $%s.\n, humanize.Comma(6582491)) // You owe $6,582,491.行为示例0 → 0、100 → 100、1000 → 1,000、1000000000 → 1,000,000,000、-100000 → -100,000。实现要点comma.go负号单独提取负数绝对值参与分组从低位起每 3 位切一段不足 3 位时左侧补0再拼接特殊处理math.MinInt64因为MinInt64无法被安全取负直接返回硬编码字符串-9,223,372,036,854,775,808。5.2Commaf与CommafWithDigitsfloat64humanize.Commaf(834142.32) // 834,142.32 humanize.CommafWithDigits(834142.32, 1) // 834,142.3Commafcomma.go先按strconv.FormatFloat(v, f, -1, 64)转字符串再拆分整数部分分组小数部分原样保留CommafWithDigits进一步用stripTrailingDigits限制小数位数。5.3BigCommabig.Int对math/big大整数做同样分组的BigCommacomma.go适合超大数值展示。六、Ftoa去掉尾随零的浮点格式化Go 自带的%f会固定输出 6 位小数产生2.240000、2.000000这种机器味很重的表达go-humanize 用Ftoa解决fmt.Printf(%f, 2.24) // 2.240000 fmt.Printf(%s, humanize.Ftoa(2.24)) // 2.24 fmt.Printf(%f, 2.0) // 2.000000 fmt.Printf(%s, humanize.Ftoa(2.0)) // 2实现ftoa.go先按f格式输出 6 位小数再通过stripTrailingZeros从右向左删除尾随0遇到小数点也一并删掉最终得到无尾随零的紧凑字符串。配套的FtoaWithDigits(num, digits)则先把结果截断到指定小数位数再去零例如FtoaWithDigits(3.14159, 2)得到3.14。七、SI notation科学计数的人类友好化7.1 正向输出SI/SIWithDigitshumanize.SI(0.00000000223, M) // 2.23 nMSI(input, unit)会把任意数量级的值换算到最合适的 SI 前缀上前缀取自完整的国际单位制表si.go覆盖从qquecto10⁻³⁰到Qquetta10³⁰的 21 个量级其中包含小量级nnano10⁻⁹、µmicro10⁻⁶、mmilli10⁻³无量级10⁰大量级kkilo10³、Mmega10⁶、Ggiga10⁹、Ttera10¹²等。数值部分复用Ftoa去除尾随零因此SI(1000000, B)输出1 MB。SIWithDigits(input, decimals, unit)可额外限制小数位数。7.2 计算层ComputeSIComputeSI(input) (float64, string)si.go返回调整后的数值 前缀算法为取绝对值 → 以 10 为底对数向下取整 → 再向下归整到 3 的倍数Floor(exponent/3)*3→ 数值除以对应 10 的幂。它特殊处理了换算后恰好为1000.0的情况此时进位例如1000 k归一为1 M。7.3 反向解析ParseSIParseSI(2.2345 pF) - (2.2345e-12, F, nil)通过init()中动态构建的正则si.go拆出数字、前缀与单位再用反向前缀表revSIPrefixTable值为10^指数还原数值。八、English 子包英文复数与词列连接humanize/english子包提供面向英文文案的辅助函数原文档README.markdown给出了完整示例8.1 复数english.PluralWord(1, object, ) // object english.PluralWord(42, object, ) // objects english.PluralWord(2, bus, ) // buses english.PluralWord(99, locus, loci) // loci english.Plural(1, object, ) // 1 object english.Plural(42, object, ) // 42 objects english.Plural(2, bus, ) // 2 buses english.Plural(99, locus, loci) // 99 lociPluralWord只返回单词本身单数或复数形式数量为 1 时返回单数原词否则按规则加s第三个参数允许提供不规则复数如locus → lociPlural返回数字 空格 单词的完整表达。8.2 词列Word seriesenglish.WordSeries([]string{foo}, and) // foo english.WordSeries([]string{foo, bar}, and) // foo and bar english.WordSeries([]string{foo, bar, baz}, and) // foo, bar and baz english.OxfordWordSeries([]string{foo, bar, baz}, and) // foo, bar, and bazWordSeries用连接词如and拼接英文列表超过两个元素时用逗号分隔并在最后一个元素前插入连接词OxfordWordSeries额外在连接词前加上牛津逗号foo, bar, and baz。注在 Tempo 仓库的 vendor 目录中vendor/github.com/dustin/go-humanize 固化的是核心包文件bytes.go、comma.go、ftoa.go、si.go、times.go、ordinals.go、big.go、bigbytes.go、number.go、humanize.go等english子包按原文档说明位于humanize/english本文以其文档示例为准。九、在 Tempo 仓库中的实际使用场景go-humanize 在 Tempo 中以 vendor 依赖形式存在go.mod 声明github.com/dustin/go-humanize v1.0.1vendor/modules.txt 记录其 vendor 条目。它的典型价值在于把调试输出变得直观例如在 modules/generator/processor/spanmetrics/spanmetrics_test.go 的测试日志中通过humanize.Bytes(uint64(batch.Size()))打印 batch 大小让82854982这样的原始字节数在测试输出里变成一眼可读的83 MB形式。这种数字 → 人类可读字符串的转换在分布式追踪系统的日常运维中尤其有用块block大小、批次字节数、历史时间戳、基数统计等原始数值经过Bytes/Time/Comma格式化后日志与指标面板的可读性会显著提升。你可以在自己的监控组件或工具链中以完全相同的方式引入并使用这套 API。十、小结与最佳实践字节展示日志与 UI 中默认推荐IBytesIEC 语义避免 1024/1000 混淆面向客户或通用场景可选BytesSI 语义需要还原数值时用ParseBytes配合完整后缀表。相对时间默认Time的档位模板已经足够自然如果产品有特殊粒度需求如精确到天、弱化分钟用CustomRelTime传入自定义RelTimeMagnitude分档表即可注意模板中%s方向标签与%d数量的占位约定。数字格式化整数分组用Comma浮点分组用Commaf去除尾随零用Ftoa跨数量级换算用SI/SIWithDigits超大数使用BigComma/BigBytes。英文文案拼接日志或错误提示中的数量词时用english.Plural/PluralWord并记得为不规则名词提供第三个参数。引用方式本文全部实现细节均可直接在仓库的 vendor/github.com/dustin/go-humanize 目录下逐文件核对涉及的具体文件包括 bytes.go、times.go、comma.go、ftoa.go、si.go 与 ordinals.go。【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表