20260116_142217_135

This commit is contained in:
2026-01-16 14:22:35 +08:00
parent e01a026c7a
commit 704058485c
6 changed files with 244 additions and 47 deletions

View File

@@ -14,3 +14,12 @@ java -cp "runtime:libs/*" Main
公网访问接口
https://console.winboll.cc/authcenter/ping
启动程序时通过参数指定日志级别,支持的级别为  ALL / FINE / INFO / WARNING / SEVERE 示例
# 仅输出INFO及以上级别日志
java -cp xxx Main -log:INFO
# 输出所有级别日志(默认)
java -cp xxx Main -log:ALL
# 仅输出错误日志
java -cp xxx Main -log:SEVERE

View File

@@ -29,7 +29,7 @@ trap 'on_exit' SIGABRT SIGSEGV SIGILL SIGTERM
# 4. 启动服务+日志输出
stty -echoctl # 屏蔽^C视觉印记
echo -e "\n🚀 服务启动中输入help查指令输入exit优雅停机"
java -cp "$CLASSPATH" "$MAIN_CLASS" 2> "$BASE_DIR/logs/error.log" # 异常日志写入文件
java -cp "$CLASSPATH" "$MAIN_CLASS" -v -log:ALL &> "$BASE_DIR/logs/main.log"
# 5. Java退出后 强制重置终端+脚本退出,彻底无残留
if [ $STOP_SIGNAL -eq 0 ]; then

View File

@@ -0,0 +1,40 @@
#!/bin/bash
# Termux专属启动脚本 编译+启动 解决exit后卡空行/需手动^C问题
# 优化增加JVM内存限制、进程异常捕获、日志输出
cd "$(dirname "${BASH_SOURCE[0]}")" || { echo "目录切换失败"; exit 1; }
BASE_DIR=$(cd .. && pwd)
# 1. 编译校验
BUILD_SH="$BASE_DIR/bash/build_class.sh"
bash "$BUILD_SH" || { echo "编译失败退出"; exit 1; }
# 2. 类路径配置
CLASSPATH="$BASE_DIR/runtime"
[ -d "$BASE_DIR/libs" ] && CLASSPATH="$CLASSPATH:$BASE_DIR/libs/*"
MAIN_CLASS="Main"
# 3. 关键优化JVM内存限制避免内存耗尽+ 异常日志输出
export _JAVA_OPTIONS="-Xmx128m -Xms64m" # 限制最大堆内存128M初始堆64M适配Termux
STOP_SIGNAL=0
# 捕获进程异常终止信号
on_exit() {
STOP_SIGNAL=1
echo -e "\n⚠ 服务异常终止,可查看日志排查问题"
stty sane
exit 1
}
trap 'on_exit' SIGABRT SIGSEGV SIGILL SIGTERM
# 4. 启动服务+日志输出
stty -echoctl # 屏蔽^C视觉印记
echo -e "\n🚀 服务启动中输入help查指令输入exit优雅停机"
java -cp "$CLASSPATH" "$MAIN_CLASS" -v -log:INFO &> "$BASE_DIR/logs/main.log"
# 5. Java退出后 强制重置终端+脚本退出,彻底无残留
if [ $STOP_SIGNAL -eq 0 ]; then
echo -e "\n✅ 服务优雅停机"
fi
stty sane
exit $?

View File

@@ -1,39 +1,79 @@
package cc.winboll;
import cc.winboll.util.MainUtils;
import java.util.Date;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* 日志工具类对接Java原生java.util.logging简化分级调用+统一格式化输出
* 适配Java8语法兼容Android API30支持异常堆栈打印
* 适配Java7语法兼容Android API30支持异常堆栈打印
* 支持通过启动参数动态控制全局日志级别、-v参数控制终端同步输出
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/14 00:00:00
* @LastEditTime 2026/01/15 02:42:35
* @LastEditTime 2026/01/18 19:30:00
*/
public class LogUtils {
// 全局日志实例,绑定当前工具类名
private static final Logger LOGGER = Logger.getLogger(LogUtils.class.getName());
// 全局控制台处理器(保留引用,用于动态修改级别/添加移除)
private static ConsoleHandler consoleHandler;
// 静态代码块初始化日志配置(仅执行一次,全局生效)
static {
// 关闭父处理器,避免日志重复输出
LOGGER.setUseParentHandlers(false);
// 自定义控制台输出处理器,绑定格式化规则
ConsoleHandler consoleHandler = new ConsoleHandler();
// 初始化控制台处理器,但暂不添加到Logger由-v参数控制
consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new CustomLogFormatter());
LOGGER.addHandler(consoleHandler);
// 默认日志级别(全量输出,可按需调整屏蔽低级别日志)
LOGGER.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
// 默认日志级别(全量输出,可通过启动参数覆盖)
setGlobalLogLevel(Level.ALL);
}
// 私有化构造方法,禁止外部实例化
private LogUtils() {}
/**
* 工具方法判断Logger是否包含指定HandlerJava7兼容
* @param handler 目标Handler
* @return 包含返回true否则返回false
*/
private static boolean containsHandler(Handler handler) {
if (handler == null) {
return false;
}
Handler[] handlers = LOGGER.getHandlers();
for (Handler h : handlers) {
if (h == handler) { // 引用比较,确保是同一个处理器实例
return true;
}
}
return false;
}
/**
* 动态设置全局日志级别 + 控制终端输出开关(-v参数驱动
* @param level 目标日志级别ALL/FINE/INFO/WARNING/SEVERE
*/
public static void setGlobalLogLevel(Level level) {
LOGGER.setLevel(level);
// 根据 MainUtils 中 -v 参数状态,动态控制控制台输出
if (MainUtils.isVerbose()) {
if (!containsHandler(consoleHandler)) {
LOGGER.addHandler(consoleHandler);
}
consoleHandler.setLevel(level);
} else {
if (containsHandler(consoleHandler)) {
LOGGER.removeHandler(consoleHandler);
}
}
}
// 调试日志(细粒度开发调试信息,上线可屏蔽)
public static void d(String tag, String msg) {
LOGGER.fine(String.format("[%s] %s", tag, msg));
@@ -59,7 +99,7 @@ public class LogUtils {
LOGGER.warning(String.format("[%s] %s", tag, msg));
}
// 新增:警告日志-重载版(适配异常场景调用)
// 警告日志-重载版(适配异常场景调用)
public static void w(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), throwable);
}
@@ -69,7 +109,7 @@ public class LogUtils {
LOGGER.severe(String.format("[%s] %s", tag, msg));
}
// 新增:错误日志-重载版(核心补充,支持异常堆栈打印)
// 错误日志-重载版(核心补充,支持异常堆栈打印)
public static void e(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), throwable);
}

View File

@@ -11,15 +11,19 @@ import cc.winboll.util.InitCheckUtils;
import cc.winboll.util.MainUtils;
import cc.winboll.util.ServerUtils;
import java.io.IOException;
import java.util.logging.Level;
/**
* 程序入口类负责初始化运行环境检测与AuthCenter HTTP服务启动
* 适配Java7语言规范兼容Android API30运行环境支持优雅停机
* 启动参数规则:1. -detail(可选,显示详细环境信息) 2. 服务器地址(可选,覆盖默认/INI配置)
* 启动参数规则:
* 1. -detail(可选,显示详细环境信息)
* 2. 服务器地址(可选,无-前缀,覆盖默认/INI配置)
* 3. -log:LEVEL(可选指定日志级别支持ALL/FINE/INFO/WARNING/SEVERE)
* 支持控制台输入交互输入help查帮助、输入exit退出、其他指令提示未识别
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/14 00:00:00
* @LastEditTime 2026/01/18 11:00:00
* @LastEditTime 2026/01/18 16:30:00
*/
public class Main {
private static final String TAG = "AuthCenterMain";
@@ -27,28 +31,44 @@ public class Main {
private static int httpPort;
public static void main(String[] args) {
// 1. 优先解析所有启动参数到MainUtils的argMap全局唯一入口
MainUtils.parseArgs(args);
// 2. 加载INI配置
IniConfigUtils.loadRootConfig();
LogUtils.d(TAG, "【函数调用】main(),传入参数:" + MainUtils.arrayToString(args));
boolean showDetailEnv = MainUtils.parseDetailArg(args);
String serverUrl = MainUtils.getFinalServerUrl(args);
// 3. 从argMap获取日志级别并设置确保后续日志输出符合预期
Level logLevel = MainUtils.parseLogLevelArg();
LogUtils.setGlobalLogLevel(logLevel);
// 4. 输出启动参数与日志级别信息,此时日志级别已生效
LogUtils.d(TAG, "【函数调用】main(),传入参数:" + MainUtils.arrayToString(args));
LogUtils.i(TAG, "当前全局日志级别已设置为:" + logLevel.getName());
// 5. 从argMap读取参数不再传递args
boolean showDetailEnv = MainUtils.parseDetailArg();
String serverUrl = MainUtils.getFinalServerUrl();
httpPort = MainUtils.getHttpPortFromConfig();
// 6. 环境信息打印与工具类初始化
EnvInfoUtils.printEnvReport(showDetailEnv);
initServerUtils(serverUrl);
EmailSendUtils.initEmailConfig();
// 7. 初始化校验,不通过则退出
boolean initCheckPass = InitCheckUtils.checkAllInitConfig();
if (!initCheckPass) {
LogUtils.e(TAG, "初始化校验未通过,程序退出");
System.exit(1);
}
System.out.println("Hello World!");
// 8. 启动HTTP服务
startAuthCenterHttpService();
ConsoleInputUtils.initConsoleScanner();
consoleBlockForService();
// 9. 资源释放与程序退出
MainUtils.releaseAllResources(httpService);
LogUtils.i(TAG, "【函数结束】main(),程序正常退出");
}

View File

@@ -3,81 +3,162 @@ package cc.winboll.util;
import cc.winboll.LogUtils;
import cc.winboll.auth.MailAuthUtils;
import cc.winboll.service.AuthCenterHttpService;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* Main类通用工具抽离负责参数解析、地址处理、资源释放
* 适配Java7 + Android API30无状态设计纯静态方法
* 支持 -key:value 格式参数解析基于Map存储参数供外部模块查询
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/18 11:00:00
* @LastEditTime 2026/01/18 17:00:00
*/
public class MainUtils {
// 常量迁移:与业务无关的通用常量
public static final String ARG_DETAIL = "-detail";
public static final String ARG_DETAIL = "detail";
public static final String ARG_LOG_LEVEL = "log";
public static final String ARG_SERVER_URL = "serverUrl";
// 新增:-v 参数常量,控制终端同步输出日志
public static final String ARG_VERBOSE = "v";
public static final String INI_SERVER_SECTION = "ServerConfig";
public static final String INI_SERVER_KEY = "server_base_url";
public static final String INI_HTTP_SECTION = "HttpServiceConfig";
public static final String INI_HTTP_PORT_KEY = "http_server_port";
public static final int DEFAULT_HTTP_PORT = 8080;
public static final String DEFAULT_SERVER_URL = "http://localhost:8080";
public static final Level DEFAULT_LOG_LEVEL = Level.ALL;
// 参数存储Mapkey=参数名value=参数值
private static final Map<String, String> argMap = new HashMap<String, String>();
private static final String TAG = "MainUtils";
/**
* 解析启动参数,判断是否输出详细环境信息
* 解析启动参数,基于 -key:value 格式存储到argMap
* @param args 启动参数数组
*/
public static boolean parseDetailArg(String[] args) {
LogUtils.d(TAG, "【函数调用】parseDetailArg()入参args" + arrayToString(args));
public static void parseArgs(String[] args) {
LogUtils.d(TAG, "【函数调用】parseArgs()入参args" + arrayToString(args));
if (args == null || args.length == 0) {
return false;
LogUtils.d(TAG, "【函数返回】parseArgs(),无启动参数,直接返回");
return;
}
for (String arg : args) {
String trimArg = arg.trim();
if (ARG_DETAIL.equalsIgnoreCase(trimArg)) {
LogUtils.i(TAG, "【参数检测】匹配-detail参数将输出详细环境信息");
return true;
// 匹配 -* 前缀的参数
if (trimArg.startsWith("-")) {
String[] argParts = trimArg.substring(1).split(":", 2);
String key = argParts[0];
String value = argParts.length > 1 ? argParts[1] : "true";
switch (key) {
case ARG_DETAIL:
argMap.put(ARG_DETAIL, value);
LogUtils.i(TAG, "【参数解析】匹配-detail参数" + value);
break;
case ARG_LOG_LEVEL:
argMap.put(ARG_LOG_LEVEL, value.toUpperCase());
LogUtils.i(TAG, "【参数解析】匹配-log参数级别" + value.toUpperCase());
break;
// 新增:识别 -v 参数
case ARG_VERBOSE:
argMap.put(ARG_VERBOSE, value);
LogUtils.i(TAG, "【参数解析】匹配-v参数终端同步日志输出已开启");
break;
default:
LogUtils.w(TAG, "【参数解析】未知参数:" + trimArg);
break;
}
} else {
// 非 -* 前缀的参数,判定为服务器地址
argMap.put(ARG_SERVER_URL, trimArg);
LogUtils.i(TAG, "【参数解析】匹配服务器地址参数:" + trimArg);
}
}
return false;
LogUtils.d(TAG, "【函数返回】parseArgs()参数解析完成argMap" + argMap.toString());
}
/**
* 外部模块查询参数值
* @param key 参数名
* @return 参数值无则返回null
*/
public static String getArgValue(String key) {
return argMap.get(key);
}
/**
* 解析是否输出详细环境信息
*/
public static boolean parseDetailArg() {
LogUtils.d(TAG, "【函数调用】parseDetailArg()从argMap查询参数");
String value = argMap.get(ARG_DETAIL);
boolean result = "true".equalsIgnoreCase(value);
LogUtils.d(TAG, "【函数返回】parseDetailArg(),返回:" + result);
return result;
}
/**
* 新增:判断是否开启终端同步日志输出
*/
public static boolean isVerbose() {
LogUtils.d(TAG, "【函数调用】isVerbose()从argMap查询-v参数状态");
String value = argMap.get(ARG_VERBOSE);
boolean result = "true".equalsIgnoreCase(value);
LogUtils.d(TAG, "【函数返回】isVerbose(),返回:" + result);
return result;
}
/**
* 解析日志级别参数
*/
public static Level parseLogLevelArg() {
LogUtils.d(TAG, "【函数调用】parseLogLevelArg()从argMap查询参数");
String levelStr = argMap.get(ARG_LOG_LEVEL);
if (levelStr == null || levelStr.trim().isEmpty()) {
LogUtils.d(TAG, "【函数返回】parseLogLevelArg(),无日志级别参数,返回默认级别:" + DEFAULT_LOG_LEVEL);
return DEFAULT_LOG_LEVEL;
}
try {
Level level = Level.parse(levelStr);
LogUtils.d(TAG, "【函数返回】parseLogLevelArg(),返回解析级别:" + level);
return level;
} catch (IllegalArgumentException e) {
LogUtils.w(TAG, "日志级别参数非法:" + levelStr + ",使用默认级别" + DEFAULT_LOG_LEVEL, e);
LogUtils.d(TAG, "【函数返回】parseLogLevelArg(),参数非法返回默认级别:" + DEFAULT_LOG_LEVEL);
return DEFAULT_LOG_LEVEL;
}
}
/**
* 整合多来源服务器地址,优先级 启动参数 > INI配置 > 默认地址
*/
public static String getFinalServerUrl(String[] args) {
public static String getFinalServerUrl() {
LogUtils.d(TAG, "【函数调用】getFinalServerUrl(),整合服务器地址(启动参数>INI>默认)");
String argUrl = parseServerUrlArg(args);
// 从argMap获取启动参数中的服务器地址
String argUrl = argMap.get(ARG_SERVER_URL);
if (argUrl != null && !argUrl.trim().isEmpty()) {
LogUtils.i(TAG, "服务器地址优先级1启动参数 → " + argUrl);
LogUtils.d(TAG, "【函数返回】getFinalServerUrl(),返回启动参数地址:" + argUrl);
return argUrl;
}
String iniUrl = IniConfigUtils.getConfigValue(INI_SERVER_SECTION, INI_SERVER_KEY);
if (iniUrl != null && !iniUrl.trim().isEmpty()) {
LogUtils.i(TAG, "服务器地址优先级2INI配置 → " + iniUrl);
LogUtils.d(TAG, "【函数返回】getFinalServerUrl()返回INI配置地址" + iniUrl);
return iniUrl;
}
LogUtils.i(TAG, "服务器地址优先级3默认地址 → " + DEFAULT_SERVER_URL);
LogUtils.d(TAG, "【函数返回】getFinalServerUrl(),返回默认地址:" + DEFAULT_SERVER_URL);
return DEFAULT_SERVER_URL;
}
/**
* 从启动参数提取自定义服务器地址
*/
private static String parseServerUrlArg(String[] args) {
LogUtils.d(TAG, "【函数调用】parseServerUrlArg()入参args" + arrayToString(args));
if (args == null || args.length == 0) {
return null;
}
for (String arg : args) {
String trimArg = arg.trim();
if (!ARG_DETAIL.equalsIgnoreCase(trimArg) && !trimArg.isEmpty()) {
LogUtils.i(TAG, "【参数检测】匹配自定义服务器地址,覆盖默认值:" + trimArg);
return trimArg;
}
}
return null;
}
/**
* 补全服务器地址HTTP/HTTPS协议头
*/
@@ -85,16 +166,19 @@ public class MainUtils {
LogUtils.d(TAG, "【函数调用】completeServerUrlProtocol(),入参原始地址:" + url);
if (url == null || url.trim().isEmpty()) {
LogUtils.w(TAG, "原始地址为空,使用默认服务器地址:" + DEFAULT_SERVER_URL);
LogUtils.d(TAG, "【函数返回】completeServerUrlProtocol(),返回默认地址:" + DEFAULT_SERVER_URL);
return DEFAULT_SERVER_URL;
}
String trimUrl = url.trim();
if (trimUrl.startsWith("http://") || trimUrl.startsWith("https://")) {
LogUtils.d(TAG, "【函数返回】completeServerUrlProtocol(),地址协议头完整,返回原地址:" + trimUrl);
return trimUrl;
}
String newUrl = "http://" + trimUrl;
LogUtils.w(TAG, "服务器地址协议头缺失,自动补全为:" + newUrl);
LogUtils.d(TAG, "【函数返回】completeServerUrlProtocol(),补全协议头后返回:" + newUrl);
return newUrl;
}
@@ -106,19 +190,23 @@ public class MainUtils {
String portStr = IniConfigUtils.getConfigValue(INI_HTTP_SECTION, INI_HTTP_PORT_KEY);
if (portStr == null || portStr.trim().isEmpty()) {
LogUtils.i(TAG, "未读取到HTTP端口配置使用默认端口" + DEFAULT_HTTP_PORT);
LogUtils.d(TAG, "【函数返回】getHttpPortFromConfig(),返回默认端口:" + DEFAULT_HTTP_PORT);
return DEFAULT_HTTP_PORT;
}
try {
int port = Integer.parseInt(portStr.trim());
if (port > 0 && port <= 65535) {
LogUtils.i(TAG, "从INI读取HTTP端口成功" + port);
LogUtils.d(TAG, "【函数返回】getHttpPortFromConfig()返回INI配置端口" + port);
return port;
} else {
LogUtils.w(TAG, "HTTP端口配置非法使用默认端口" + DEFAULT_HTTP_PORT);
LogUtils.d(TAG, "【函数返回】getHttpPortFromConfig(),端口非法返回默认值:" + DEFAULT_HTTP_PORT);
return DEFAULT_HTTP_PORT;
}
} catch (Exception e) {
LogUtils.w(TAG, "HTTP端口配置解析失败使用默认端口" + DEFAULT_HTTP_PORT);
LogUtils.w(TAG, "HTTP端口配置解析失败使用默认端口" + DEFAULT_HTTP_PORT, e);
LogUtils.d(TAG, "【函数返回】getHttpPortFromConfig(),解析异常返回默认值:" + DEFAULT_HTTP_PORT);
return DEFAULT_HTTP_PORT;
}
}