Java中慎用yyyy与YYYY

开始之前,先看一个单元测试跑出的结果:

    @Test
    public void should_get_string_when_given_date() {
        Long timeFor20191230 = 1577635200000L;
        Date date = new Date(timeFor20191230);

        String timeLower = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
        String timeUpper = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(date);

        System.out.println("yyyy-MM-dd HH:mm:ss" + " 对应 " + timeLower);
        System.out.println("YYYY-MM-dd HH:mm:ss" + " 对应 " + timeUpper);


        String timeForHourLower = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date);
        System.out.println("YYYY-MM-dd hh:mm:ss" + " 对应 " + timeForHourLower);
    }

第一个测试输出结果如下:

yyyy-MM-dd HH:mm:ss 对应 2019-12-30 00:00:00
YYYY-MM-dd HH:mm:ss 对应 2020-12-30 00:00:00
YYYY-MM-dd hh:mm:ss 对应 2019-12-30 12:00:00

也就是说yyyy与YYYY输出的年份不一致。

从上边的结果看,发现同一个日期按 "yyyy-MM-dd HH:mm:ss” 与 "YYYY-MM-dd HH:mm:ss” 格式化后,得到的可能不一致。

原因:

YYYY 代表 Week Year

yyyy 代表year

再看下解析:

    @Test
    public void should_get_date_when_given_string() throws Exception {
        Date timeLower = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2019-12-30 00:00:00");
        Date timeUpper = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").parse("2019-12-30 00:00:00");

        System.out.println("yyyy-MM-dd HH:mm:ss" + " 对应 " + timeLower.toString());
        System.out.println("YYYY-MM-dd HH:mm:ss" + " 对应 " + timeUpper.toString());


        Date timeForHourLower = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse("2019-12-30 00:00:00");
        System.out.println("YYYY-MM-dd hh:mm:ss" + " 对应 " + timeForHourLower.toString());
    }

此时解析的结果如下:

yyyy-MM-dd HH:mm:ss 对应 Mon Dec 30 00:00:00 CST 2019
YYYY-MM-dd HH:mm:ss 对应 Sun Dec 30 00:00:00 CST 2018
YYYY-MM-dd hh:mm:ss 对应 Mon Dec 30 00:00:00 CST 2019

这次更离谱

这里补充一下,Oracle里不区分大小写,对应的 yyyy-MM-dd HH24:mi:ss代表24小时制,yyyy-MM-dd HH:mi:ss 代表12小时制。

HH代表24小时制

hh代表12小时制

2025-04-01 14:31:02,837 process reaper (pid 189) ERROR Recursive call to appender rolling 2025-04-01 14:31:02,910 process reaper (pid 189) ERROR Recursive call to appender rolling 2025-04-01 14:31:02,915 process reaper (pid 189) ERROR Recursive call to appender rolling 2025-04-01 14:31:02,919 process reaper (pid 189) ERROR Recursive call to appender rolling {"@timestamp":"2025-04-01T14:31:02.829Z", "log.level":"ERROR", "message":"uncaught exception in thread [process reaper (pid 189)]", "ecs.version": "1.2.0","service.name":"ES_ECS","event.dataset":"elasticsearch.server","process.thread.name":"process reaper (pid 189)","log.logger":"org.elasticsearch.bootstrap.ElasticsearchUncaughtExceptionHandler","elasticsearch.node.name":"elasticsearch-master-0","error.type":"java.security.AccessControlException","error.message":"access denied (\"java.lang.RuntimePermission\" \"modifyThread\")","error.stack_trace":"java.security.AccessControlException: access denied (\"java.lang.RuntimePermission\" \"modifyThread\")\n\tat java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:485)\n\tat java.base/java.security.AccessController.checkPermission(AccessController.java:1068)\n\tat java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:411)\n\tat org.elasticsearch.securesm@8.5.1/org.elasticsearch.secure_sm.SecureSM.checkThreadAccess(SecureSM.java:166)\n\tat org.elasticsearch.securesm@8.5.1/org.elasticsearch.secure_sm.SecureSM.checkAccess(SecureSM.java:120)\n\tat java.base/java.lang.Thread.checkAccess(Thread.java:2360)\n\tat java.base/java.lang.Thread.setDaemon(Thread.java:2308)\n\tat java.base/java.lang.ProcessHandleImpl.lambda$static$0(ProcessHandleImpl.java:103)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.<init>(ThreadPoolExecutor.java:637)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:928)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.processWorkerExit(ThreadPoolExecutor.java:1021)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1158)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)\n\tat java.base/java.lang.Thread.run(Thread.java:1589)\n\tat java.base/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:186)\n"} ERROR: [1] bootstrap checks failed. You must address the points described in the following [1] lines before starting Elasticsearch.
最新发布
04-02
<think>嗯,用户提供的日志中有多个错误信息,需要逐步分析。首先,注意到重复出现的“Recursive call to appender rolling”错误,这通常日志框架的配置有关,比如Log4j的Appender被递归调用,可能是因为配置错误或者资源竞争。然后,另一个关键错误是Java的AccessControlException,提示缺少“modifyThread”的权限,说明Elasticsearch在安全管理器下运行时权限不足。此外,还有bootstrap检查失败,说明系统环境不满足Elasticsearch的启动要求。 首先,处理“Recursive call to appender rolling”问题。这可能是由于日志配置中Appender的错误引用或循环依赖。需要检查log4j2.properties或相关配置文件,确保每个Logger的Appender配置正确,没有重复或循环调用。比如,某个Appender可能错误地引用了自己,导致无限递归。此外,检查是否有多个日志框架冲突,比如同时使用了Log4j和Slf4j,可能会引发问题。 接下来是Java安全异常。错误信息指出缺少“modifyThread”的RuntimePermission。Elasticsearch使用自己的安全管理器SecureSM,限制了某些操作。当进程收割线程(process reaper)尝试修改线程属性时,由于权限不足被拒绝。这通常发生在Elasticsearch运行在严格的安全策略下。需要检查JVM的安全策略文件,通常是java.policy,确保Elasticsearch的相关代码库有足够的权限。可能需要添加如下权限: ``` grant codeBase "file:/path/to/elasticsearch/-" { permission java.lang.RuntimePermission "modifyThread"; }; ``` 另外,检查Elasticsearch的启动参数,确认是否有不必要的安全管理器启用。如果问题仍然存在,可能需要临时禁用安全管理器进行测试,但生产环境中不建议这样做。 然后是bootstrap checks失败。Elasticsearch在启动时会进行多项系统检查,如内存锁定、文件描述符数量、虚拟内存设置等。根据错误信息,至少有一项检查未通过。需要查看Elasticsearch的日志中具体的bootstrap检查失败原因。常见的问题包括: 1. 内存锁定未启用:在elasticsearch.yml中设置bootstrap.memory_lock: true,并确保用户有memlock权限。 2. 文件描述符限制不足:调整系统的ulimit设置,增加nofile和nproc的值。 3. 虚拟内存不足:检查vm.max_map_count的设置,建议设置为262144或更高。 另外,检查是否以root用户运行Elasticsearch,这会导致启动失败。应该使用专用用户运行,并确保该用户有必要的文件权限。 综合以上分析,用户需要分步骤解决这些问题。首先调整日志配置,避免递归调用;然后处理安全权限问题,确保Elasticsearch有足够的权限执行线程修改;最后,通过系统配置满足bootstrap checks的要求。此外,检查所有相关的配置文件和系统设置,确保符合Elasticsearch的最佳实践。可能需要查阅Elasticsearch官方文档,确认当前版本的具体要求,特别是版本8.5.1的配置细节,因为不同版本可能有不同的默认设置或要求。</think>### 错误分析及解决方案 你提供的日志包含**Elasticsearch**运行时出现的三个关键问题: ``` 1. Recursive call to appender rolling(日志滚动递归调用错误) 2. java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "modifyThread")(权限拒绝异常) 3. Bootstrap checks failed(启动检查失败) ``` --- #### **一、Recursive call to appender rolling** **原因**: - 日志框架(如Log4j)的`Appender`配置存在**递归调用**,通常由以下情况引发: - 日志配置文件(如`log4j2.properties`)中多个`Appender`相互引用 - 日志输出目标(如文件、控制台)的资源竞争或死锁 - 日志滚动策略(Rolling)配置错误 **解决方案**: 1. **检查日志配置文件**: - 定位`log4j2.properties`或`log4j2.xml`,确认所有`Appender`定义无循环依赖。 - 确保每个`Logger`的`AppenderRef`指向正确的目标。 - 示例: ```properties # 正确配置示例 appender.rolling.type = RollingFile appender.rolling.name = rolling appender.rolling.filePattern = logs/app-%d{yyyy-MM-dd}.log appender.rolling.policies.type = Policies appender.rolling.policies.time.type = TimeBasedTriggeringPolicy rootLogger.appenderRef.rolling.ref = rolling ``` 2. **避免多线程竞争**: - 添加`<AsyncLogger>`或使用异步日志库(如`Disruptor`)减少资源竞争。 3. **更新日志框架版本**: - 若使用旧版Log4j(如2.x以下),升级到最新稳定版修复已知问题。 --- #### **二、java.security.AccessControlException** **原因**: - Elasticsearch的安全管理器(`SecureSM`)阻止了`modifyThread`权限的操作。 - 进程收割线程(`process reaper`)尝试修改线程属性时触发安全策略限制。 **解决方案**: 1. **修改JVM安全策略**: - 在`java.policy`文件中添加权限: ```java grant { permission java.lang.RuntimePermission "modifyThread"; }; ``` - 若使用Elasticsearch Docker镜像,需挂载自定义策略文件并指定JVM参数: ```bash -Djava.security.policy=/path/to/custom.policy ``` 2. **调整Elasticsearch启动参数**: - 在`jvm.options`中放宽安全限制(慎用): ```bash -Djava.security.manager=allow ``` 3. **检查Security Manager配置**: - 确认`elasticsearch.yml`中未启用过严格的安全模式: ```yaml es.security.manager.enabled: false ``` --- #### **三、Bootstrap checks failed** **原因**: - Elasticsearch启动时检测到**系统环境不满足要求**,常见问题包括: - 内存锁定(Memory Lock)未启用 - 文件描述符(File Descriptor)数量不足 - 虚拟内存(Virtual Memory)配置错误 **解决方案**: 1. **启用内存锁定**: - 在`elasticsearch.yml`中设置: ```yaml bootstrap.memory_lock: true ``` - 调整系统限制: ```bash ulimit -l unlimited ``` 2. **增加文件描述符限制**: - 修改`/etc/security/limits.conf`: ```bash elasticsearch_user - nofile 65536 elasticsearch_user - memlock unlimited ``` 3. **配置虚拟内存**: - 修改`/etc/sysctl.conf`: ```bash vm.max_map_count=262144 ``` - 执行`sysctl -p`生效。 --- ### **操作步骤总结** 1. **修复日志配置**:检查`log4j2.properties`,确保无递归Appender。 2. **调整JVM权限**:修改`java.policy`或禁用不必要的安全策略。 3. **通过Bootstrap检查**:按上述步骤配置系统参数。 4. **重启Elasticsearch**:验证日志是否正常,确保无重复错误。 --- ### 注意事项 - 生产环境中谨慎修改安全策略,优先通过日志和权限配置解决问题。 - 使用Elasticsearch官方文档验证系统要求:[Bootstr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值