📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 6.8-8.9

1. 错误异常的基本描述 #

unit [u] not supported for date math [expr] 是 Elasticsearch 在解析日期数学表达式(date math)时抛出的异常。当你在索引模式、查询条件或 API 参数中使用日期数学表达式(如 now-1dnow+2h)时,如果其中的时间单位不在 Elasticsearch 支持的范围内,就会触发此错误。Elasticsearch 的 date math 支持有限的时间单位字符,使用其他单位会导致解析失败。

常见现象 #

  • Elasticsearch 返回 HTTP 400 Bad Request 状态码,响应体中包含 ElasticsearchParseException
  • 涉及 date math 的请求失败,包括索引模式(如 logs-<date_math>)、查询中的日期范围、过期时间设置等。
  • 在 Elasticsearch 服务端日志中会记录详细的异常信息和出错的表达式。
  • 如果是通过应用程序或脚本动态生成日期表达式,可能导致批量请求失败。
  • Kibana 中如果使用包含 date math 的索引模式,可能无法正确加载数据。

典型报错与异常栈 #

该异常的典型日志形态如下:

ElasticsearchParseException: unit [x] not supported for date math [now-1x]
    at org.elasticsearch.common.time.DateMathParser.parse(DateMathParser.java:...)
    at org.elasticsearch.index.mapper.DateFieldMapper$DateFieldType.rangeQuery(DateFieldMapper.java:...)
    at org.elasticsearch.index.query.RangeQueryBuilder.doToQuery(RangeQueryBuilder.java:...)

通过 API 请求的响应通常如下:

{
  "error": {
    "root_cause": [
      {
        "type": "parse_exception",
        "reason": "unit [x] not supported for date math [now-1x]"
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "failed_shards": [...]
  },
  "status": 400
}

2. 为什么会发生这个错误 #

Elasticsearch 的 date math 解析器支持以下时间单位字符:

单位字符含义
y年 (year)
M月 (month)
w周 (week)
d日 (day)
h小时 (hour)
H小时 (hour, 24小时制)
m分钟 (minute)
s秒 (second)

常见原因包括:

  • 使用了不支持的单位字符:如 now-1xnow+2min(应该用 m 而不是 min)。
  • 使用了自然语言单位:如 now-1day(应该用 now-1d)。
  • 单位字符拼写错误:如 now-1hout(多了一个 u)。
  • 程序生成错误:自动生成 date math 表达式的程序可能使用了错误的单位映射。
  • 复数字母:如 now-2days(应该用 now-2d)。
  • 从文档复制错误:从示例或文档中复制时,可能引入了不支持的格式。

3. 如何排查和解决这个异常和解决这个异常 #

排查步骤 #

建议按以下顺序进行排查:

第一步:确认错误中的单位字符 #

# 从错误响应或日志中获取完整的 date math 表达式
# 例如:unit [x] not supported for date math [now-1x]
# 重点检查表达式中的单位字符 "x"

第二步:检查请求中的 date math 表达式 #

# 查看查询或索引模式中的 date math 表达式
curl -X GET "localhost:9200/my_index*/_search" -H 'Content-Type: application/json' -d '
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-1x"  # 错误:x 不是支持的单位
      }
    }
  }
}' 2>&1 | jq .

第三步:验证正确的 date math 表达式 #

# 测试正确的 date math 表达式
curl -X GET "localhost:9200/logs-<now-1d>/_search"  # 正确:使用 d 表示天
curl -X GET "localhost:9200/logs-<now-2h>/_search"  # 正确:使用 h 表示小时

第四步:检查程序生成逻辑 #

# Python 示例:检查单位映射是否正确
unit_mapping = {
    'minutes': 'm',   # 正确
    'hours': 'h',      # 正确
    'days': 'd',       # 正确
    # 'day': 'd'      # 避免使用复数或自然语言的键
}

def generate_date_math(offset, unit):
    es_unit = unit_mapping.get(unit)
    if not es_unit:
        raise ValueError(f"Unsupported unit: {unit}")
    return f"now-{offset}{es_unit}"

排查时需要注意的问题 #

  • 区分大小写M 表示月,m 表示分钟,不要混淆。
  • 检查表达式完整性:date math 表达式必须用尖括号包裹,如 <now-1d>,而不是 now-1d
  • 注意特殊字符转义:在 JSON 字符串中,尖括号通常不需要转义,但在某些上下文中可能需要。
  • 查看完整表达式:错误信息可能只显示部分表达式,需要查看完整请求才能定位问题。

4. 如何解决这个错误 #

常用修复思路 #

方案一:使用正确的单位字符 #

// 错误示例
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-1day"  // 错误:day 不是支持的单位
      }
    }
  }
}

// 正确示例
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-1d"  // 正确:使用 d 表示天
      }
    }
  }
}

方案二:建立单位映射白名单 #

# 建立标准的单位映射
ELASTICSEARCH_DATE_UNITS = {
    'y': '年',
    'M': '月',
    'w': '周',
    'd': '日',
    'h': '小时',
    'H': '小时(24h)',
    'm': '分钟',
    's': '秒'
}

def validate_date_math(expr):
    import re
    # 简单的正则检查 date math 表达式中的单位
    pattern = r'now[+-]?\d+([yMwdhHms])'
    match = re.search(pattern, expr)
    if match:
        unit = match.group(1)
        if unit not in ELASTICSEARCH_DATE_UNITS:
            raise ValueError(f"Unsupported unit: {unit}")

方案三:使用 ISO 8601 格式代替 date math #

// 如果不确定 date math 语法,可以使用具体的日期时间
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "2024-01-01T00:00:00Z"  // 使用具体时间而不是 date math
      }
    }
  }
}

方案四:在应用程序中添加校验 #

// Java 示例:在构造查询前校验 date math 表达式
public static void validateDateMath(String expr) {
    Set<Character> validUnits = Set.of('y', 'M', 'w', 'd', 'h', 'H', 'm', 's');
    // 简化的校验逻辑
    if (expr.contains("now")) {
        // 检查单位字符
        Pattern p = Pattern.compile("now[+-]?\\d+([yMwdhHms])");
        if (!p.matcher(expr).find()) {
            throw new IllegalArgumentException("Invalid date math expression: " + expr);
        }
    }
}

后续注意事项与推荐建议 #

  • 建立 date math 使用规范:为团队制定 date math 表达式的使用规范,明确支持的单位字符。
  • 在 CI/CD 中加入校验:对于包含 date math 的配置文件或脚本,在部署前进行语法检查。
  • 使用常量定义常用表达式:在代码中定义常用的 date math 表达式常量,避免重复构造可能出错的表达式。
  • 监控表达式错误:通过日志监控及时发现 date math 解析错误,快速定位和修复。
  • 考虑使用时间戳:对于精度要求高的场景,考虑直接使用 Unix 时间戳而不是 date math。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 提供查询历史记录和表达式分析功能,可以帮助快速定位出错的 date math 表达式。通过 Console 的查询调试工具,可以在界面上直接测试 date math 表达式,查看详细的错误信息。

  • INFINI Gateway 可以拦截和检查发往 Elasticsearch 的请求,自动检测并拒绝包含非法 date math 表达式的查询。Gateway 还提供请求重写功能,可以在表达式到达 Elasticsearch 之前自动修正常见的单位错误(如将 day 转换为 d)。

  • 对于需要频繁使用 date math 的团队,建议结合 INFINI Console 的查询分析能力和 INFINI Gateway 的请求治理能力,建立从表达式构造、验证、执行到监控的完整流程,大幅减少因单位错误导致的异常。

5. 小结 #

unit [u] not supported for date math 是一个典型的 date math 语法错误,根源在于使用了 Elasticsearch 不支持的时间单位字符。虽然报错信息直接指向单位问题,但修复时需要仔细检查整个表达式的结构,确保使用正确的单位字符(如 d 表示天,而不是 daydays)。

在实际工作中,为避免此类问题,建议在开发阶段就使用 Kibana Dev Tools 或 INFINI Console 的查询工具测试 date math 表达式,在代码中建立单位映射白名单,并使用 INFINI Gateway 作为防护层来拦截和修正错误的表达式。通过规范化和工具化的方式,可以大幅减少此类语法错误的发生。

相关错误 #

参考文档 #

附:日志上下文 #

下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:

default:
    throw new ElasticsearchParseException("unit [{}] not supported for date math [{}]", unit, mathString);