Ruby Prometheus:如何修复大写UUID引发的时间序列基数过高问题
问题背景
我们在Rails应用的config.ru里配置了Prometheus收集器:
use Prometheus::Middleware::Collector
Prometheus的核心原则之一是保持指标低基数,否则会引发性能下降,图表也会失去参考价值。该收集器实现低基数的关键代码如下:
def generate_path(env) full_path = [env['SCRIPT_NAME'], env['PATH_INFO']].join strip_ids_from_path(full_path) end def strip_ids_from_path(path) path .gsub(%r{/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?=/|$)}, '/:uuid\\1') .gsub(%r{/\d+(?=/|$)}, '/:id\\1') end
目前这段代码只能处理小写UUID,遇到大写UUID时无法识别,示例如下:
uuid = "f7ec02c0-19b0-4519-8e3e-d8ac4434a389" irb(main):024:0> strip_ids_from_path("user/#{uuid}") => "user/:uuid" irb(main):025:0> strip_ids_from_path("user/#{uuid.upcase}") => "user/F7EC02C0-19B0-4519-8E3E-D8AC4434A389"
我可以复制整个中间件并修改成strip_ids_from_path(full_path.downcase),但这种复制整段代码的修复方式不够优雅。想请教有没有更巧妙的解决方案?能否借助Rails特性来处理?
解决方案
1. 给正则添加不区分大小写修饰符
不需要复制整个中间件,只用class_eval重写特定方法,给UUID匹配正则加上i修饰符,让它同时匹配大小写字母:
Prometheus::Middleware::Collector.class_eval do def strip_ids_from_path(path) path .gsub(%r{/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?=/|$)i}, '/:uuid\\1') .gsub(%r{/\d+(?=/|$)}, '/:id\\1') end end
这种方式侵入性低,只修改核心匹配逻辑。
2. 利用Rails路由识别(推荐)
如果你的Rails应用已经定义了对应路由(比如GET /users/:uuid),可以直接复用Rails的路由系统来生成标准化路径,完全不用自己处理UUID格式:
Prometheus::Middleware::Collector.class_eval do def generate_path(env) request = ActionDispatch::Request.new(env) route = Rails.application.routes.recognize_path(request.path) # 生成标准化的路由路径模板 Rails.application.routes.url_helpers.polymorphic_path(route.except(:controller, :action), format: nil) rescue ActionController::RoutingError # 无法识别的路由回退到原逻辑 super end end
这种方式不仅能处理大小写UUID,还能自动适配其他动态参数,完全贴合Rails生态。
3. 轻量修改:调用前转小写
如果不想改动正则,也可以只重写generate_path方法,在调用原处理方法前把路径转小写:
Prometheus::Middleware::Collector.class_eval do def generate_path(env) full_path = [env['SCRIPT_NAME'], env['PATH_INFO']].join strip_ids_from_path(full_path.downcase) end end
这种方式改动最小,只修改路径处理的入口逻辑,不影响原方法的其他功能。
内容的提问来源于stack exchange,提问作者David Michael Gang
相关产品推荐
相关产品推荐

