gen_event例子:terminal_logger

本文介绍如何使用Erlang编写三个不同的终端日志管理器,并通过gen_event行为实现错误消息的日志记录功能。文章展示了如何定义、编译及运行这些日志管理器,并演示了当通知错误信息时它们是如何工作的。
定义三个terminal_logger:
$$ terminal_logger1.erl
[code]
-module(terminal_logger1).
-behaviour(gen_event).

-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3, terminate/2]).

init(_Args) ->
{ok, []}.

handle_event(ErrorMsg, State) ->
io:format("*** Error1 *** ~p~n", [ErrorMsg]),
{ok, State}.

handle_call(_Request, _State) ->
{ok, [], []}.

handle_info(_Info, _State) ->
{ok, []}.

code_change(_OlvVsn, _State, _Extra) ->
ok.

terminate(_Args, _State) ->
ok.
[/code]
$$ terminal_logger2.erl
[code]
-module(terminal_logger2).
-behaviour(gen_event).

-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3, terminate/2]).

init(_Args) ->
{ok, []}.

handle_event(ErrorMsg, State) ->
io:format("*** Error2 *** ~p~n", [ErrorMsg]),
{ok, State}.

handle_call(_Request, _State) ->
{ok, [], []}.

handle_info(_Info, _State) ->
{ok, []}.

code_change(_OlvVsn, _State, _Extra) ->
ok.

terminate(_Args, _State) ->
ok.
[/code]
$$ terminal_logger3.erl
[code]
-module(terminal_logger3).
-behaviour(gen_event).

-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3, terminate/2]).

init(_Args) ->
{ok, []}.

handle_event(ErrorMsg, State) ->
io:format("*** Error3 *** ~p~n", [ErrorMsg]),
{ok, State}.

handle_call(_Request, _State) ->
{ok, [], []}.

handle_info(_Info, _State) ->
{ok, []}.

code_change(_OlvVsn, _State, _Extra) ->
ok.

terminate(_Args, _State) ->
ok.
[/code]

编译:
[code]
Eshell> c(terminal_logger1).
Eshell> c(terminal_logger2).
Eshell> c(terminal_logger3).
[/code]

运行:
[code]
D:\erl\code>erl
Eshell V5.6.3 (abort with ^G)
1> gen_event:start_link({local, error_man}).
{ok,<0.31.0>}
2> gen_event:add_handler(error_man, terminal_logger1, []).
ok
3> gen_event:add_handler(error_man, terminal_logger2, []).
ok
4> gen_event:add_handler(error_man, terminal_logger3, []).
ok
5> gen_event:which_handlers(error_man).
[terminal_logger3,terminal_logger2,terminal_logger1]
6> gen_event:notify(error_man, "Hideto").
*** Error3 *** "Hideto"
ok
*** Error2 *** "Hideto"
7> *** Error1 *** "Hideto"
7>
[/code]
#E:\AI_System\main.py import os import sys import traceback import threading import time import logging from pathlib import Path from core.config import config from core.command_listener import start_command_listener from agent.model_manager import ModelManager from agent.cognitive_architecture import CognitiveSystem from agent.environment_interface import EnvironmentInterface def setup_logging(): """配置系统日志记录""" log_dir = Path(config.get("LOG_DIR", "logs")) log_dir.mkdir(parents=True, exist_ok=True) log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" log_level = config.get("ENVIRONMENT.LOG_LEVEL", "INFO").upper() logging.basicConfig( level=getattr(logging, log_level), format=log_format, handlers=[ logging.FileHandler(log_dir / "ai_system.log", encoding='utf-8'), logging.StreamHandler() ] ) logger = logging.getLogger("Main") logger.info("日志系统初始化完成 (级别: %s)", log_level) return logger def main(): """主函数""" logger = setup_logging() logger.info("=" * 50) logger.info("🚀 启动AI系统 - 核心认知模式") logger.info("=" * 50) logger.info("系统配置摘要:") logger.info(f"项目根目录: {config.get('DIRECTORIES.PROJECT_ROOT', '未设置')}") logger.info(f"默认模型路径: {config.get('MODEL_PATHS.TEXT_BASE', '未设置')}") logger.info(f"日志级别: {config.get('ENVIRONMENT.LOG_LEVEL', 'INFO')}") # 初始化模型管理器 try: model_manager = ModelManager( model_registry=config.get("MODEL_PATHS", {}), cache_dir=config.get("MODEL_CACHE_DIR", "model_cache"), use_gpu=config.get("ENVIRONMENT.USE_GPU", True) ) logger.info("✅ 模型管理器初始化完成") # 修复模型加载逻辑 - 确保正确加载模型 base_model_key = "TEXT_BASE" base_model_path = config.get("MODEL_PATHS", {}).get(base_model_key) if base_model_path and model_manager.load_model(base_model_key): logger.info(f"✅ 基础模型已加载: {base_model_path}") else: logger.warning(f"⚠️ 基础模型加载失败: {base_model_key}") except Exception as e: logger.error("❌ 模型管理器初始化失败: %s", str(e)) logger.error(traceback.format_exc()) return # 初始化认知系统接口 try: cognitive_system = CognitiveSystem( name=config.get("AGENT_NAME", "小蓝"), model_manager=model_manager, config=config.get("COGNITIVE_CONFIG", {}) ) logger.info("✅ 认知系统初始化完成 - 名称: %s", cognitive_system.name) except Exception as e: logger.error("❌ 认知系统初始化失败: %s", str(e)) logger.error(traceback.format_exc()) return # 初始化环境接口 try: environment_interface = EnvironmentInterface( name="环境接口", cognitive_system=cognitive_system, config={ "max_workers": config.get("MAX_WORKERS", 4), "response_timeout": config.get("AGENT_RESPONSE_TIMEOUT", 30.0), "log_level": config.get("ENVIRONMENT.LOG_LEVEL", "INFO") } ) logger.info("✅ 环境接口初始化完成") except Exception as e: logger.error("❌ 环境接口初始化失败: %s", str(e)) logger.error(traceback.format_exc()) return # 创建关闭处理函数 def shutdown_handler(): """系统关闭处理函数""" logger.info("🛑 收到关闭命令,开始关闭系统...") # 先停止命令监听器 if 'command_listener' in locals() and command_listener.running: command_listener.stop() # 然后停止环境接口 environment_interface.stop() logger.info("✅ 系统已完全关闭") sys.exit(0) # 自定义命令处理器 - 统一处理所有命令 def command_handler(command: str) -> dict: """处理用户命令 - 返回字典格式响应""" try: logger.info(f"🔄 处理命令: {command}") # 命令路由 cmd_lower = command.lower().strip() # 1. 系统命令处理 if cmd_lower in ["exit", "quit"]: return { "status": "success", "message": "接收到退出命令,正在关闭系统...", "action": "shutdown" } # 2. 其他命令转发给认知系统 logger.info("转发命令给认知系统: %s", command) response = cognitive_system.process_command(command) # 格式化输出响应 return { "status": "success", "content": response } except Exception as e: logger.error(f"❌ 命令处理错误: {str(e)}", exc_info=True) return { "status": "error", "error": f"处理命令时出错: {str(e)}", "command": command } # 启动命令监听器 - 作为唯一的输入来源 try: command_listener = start_command_listener( command_handler=command_handler, shutdown_handler=shutdown_handler ) logger.info("✅ 命令监听器已启动") except Exception as e: logger.error("❌ 命令监听器启动失败: %s", str(e)) logger.error(traceback.format_exc()) return logger.info("🌟 系统准备就绪! 输入命令控制模式 (输入 'help' 查看可用命令)") # 主循环:处理命令监听器的输出(修复卡死问题) try: heartbeat_interval = 5 # 心跳间隔(秒) last_heartbeat = time.time() while True: # 短暂休眠以避免CPU占用过高 time.sleep(0.1) # 添加心跳日志避免卡死 current_time = time.time() if current_time - last_heartbeat > heartbeat_interval: logger.debug("💗 系统心跳检测 - 运行中...") last_heartbeat = current_time # 检查是否收到退出信号 if not command_listener.running: logger.info("🛑 命令监听器已停止") break except KeyboardInterrupt: logger.info("🛑 收到中断信号,关闭系统...") shutdown_handler() except Exception as e: logger.error("❌ 主循环发生未预期错误: %s", str(e)) logger.error(traceback.format_exc()) shutdown_handler() if __name__ == "__main__": main() # E:\AI_System\core\config.py import os import sys import json import re import logging from pathlib import Path from dotenv import load_dotenv from prettytable import PrettyTable # 设置日志 logger = logging.getLogger('CoreConfig') logger.setLevel(logging.INFO) # 确保有基本日志处理器 if not logger.handlers: handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) class CoreConfig: """核心配置系统 - 增强版:支持变量解析和环境继承""" _instance = None _config = {} @classmethod def get_instance(cls): """获取单例实例""" if cls._instance is None: cls._instance = cls() return cls._instance def __init__(self): """初始化配置系统""" # 设置基础目录 self.base_dir = Path(__file__).resolve().parent.parent self.env_prefix = "AI_SYSTEM" # 敏感字段列表(在日志和输出中掩码) self.sensitive_fields = ["DB_PASSWORD", "SECRET_KEY", "API_KEY", "ACCESS_TOKEN"] # 路径类型配置键 self.path_keys = [ "LOG_DIR", "CONFIG_DIR", "MODEL_CACHE_DIR", "MODEL_BASE_PATH", "WEB_UI_DIR", "AGENT_DIR", "CORE_DIR", "MODELS_DIR", "LOGS_DIR", "TEXT_BASE", "TEXT_CHAT", "MULTIMODAL", "IMAGE_GEN", "YI_VL", "STABLE_DIFFUSION" ] # 加载配置 self._load_config() logger.info("✅ 配置系统初始化完成") # === 变量解析功能增强 === VARIABLE_PATTERN = re.compile(r'\$\{([^}]+)\}') def _resolve_variables(self, data=None, depth=0): """递归解析配置中的变量引用,防止无限递归""" MAX_DEPTH = 5 if depth > MAX_DEPTH: logger.warning("⚠️ 变量解析达到最大深度限制,可能存在循环引用") return data if data is None: data = self._config if isinstance(data, dict): for key, value in data.items(): if isinstance(value, str): data[key] = self._replace_vars(value, depth + 1) elif isinstance(value, (dict, list)): self._resolve_variables(value, depth + 1) elif isinstance(data, list): for i, item in enumerate(data): if isinstance(item, str): data[i] = self._replace_vars(item, depth + 1) elif isinstance(item, (dict, list)): self._resolve_variables(item, depth + 1) return data def _replace_vars(self, value: str, depth: int) -> str: """替换单个字符串中的变量引用(增强版)""" def replace_match(match): var_expression = match.group(1).strip() # 处理默认值语法:${VAR|default} if '|' in var_expression: var_name, default_val = var_expression.split('|', 1) var_name = var_name.strip() default_val = default_val.strip() else: var_name = var_expression default_val = None # 处理环境变量语法:ENV:VAR if var_name.startswith('ENV:'): env_var = var_name[4:] return os.environ.get(env_var, default_val or '') # 处理配置变量 try: return str(self.get(var_name, default_val)) except KeyError: return default_val or '' return self.VARIABLE_PATTERN.sub(replace_match, value) # === 分页打印功能增强 === def print_long_text(self, text, max_line_length=120, lines_per_page=20): """分页打印长文本(支持宽屏自适应)""" lines = text.split('\n') page = 1 total_pages = (len(lines) + lines_per_page - 1) // lines_per_page # 获取终端宽度 try: columns = os.get_terminal_size().columns max_line_length = min(max_line_length, columns - 1) except OSError: pass # 无法获取终端宽度,使用默认值 for i, line in enumerate(lines): # 长行自动换行 wrapped_lines = [] while len(line) > max_line_length: # 查找最后一个空格位置进行优雅换行 space_pos = line.rfind(' ', 0, max_line_length) if space_pos == -1: space_pos = max_line_length wrapped_lines.append(line[:space_pos]) line = line[space_pos + 1:] wrapped_lines.append(line) for wrapped_line in wrapped_lines: print(wrapped_line) # 分页控制 if (i + 1) % lines_per_page == 0 and (i + 1) < len(lines): input(f"\n--- 第 {page}/{total_pages} 页 (按Enter继续) ---") page += 1 print(f"\n--- 结束 (共 {page} 页) ---") # === 路径验证增强 === def _validate_paths(self): """验证并创建缺失的关键路径(增强错误处理)""" # 1. 验证目录路径 dir_paths = [ self.get("LOG_DIR"), self.get("CONFIG_DIR"), self.get("MODEL_CACHE_DIR"), self.get("MODEL_BASE_PATH"), self.get("WEB_UI_DIR"), self.get("AGENT_DIR"), self.get("DIRECTORIES.PROJECT_ROOT") ] for path in dir_paths: if path: try: resolved_path = Path(path) if not resolved_path.exists(): resolved_path.mkdir(parents=True, exist_ok=True) logger.info(f"📁 创建缺失目录: {resolved_path}") except Exception as e: logger.error(f"❌ 创建目录失败: {path} - {str(e)}") # 2. 验证模型路径 model_paths = self.get("MODEL_PATHS", {}) for model_type, path in model_paths.items(): if path: try: resolved_path = Path(path) if not resolved_path.exists(): resolved_path.mkdir(parents=True, exist_ok=True) logger.info(f"📁 创建缺失模型路径: {resolved_path}") except Exception as e: logger.error(f"❌ 创建模型路径失败: {path} - {str(e)}") # === 配置加载顺序优化 === def _load_config(self): """加载所有配置(优化加载顺序)""" try: # 1. 设置默认值 self._set_defaults() # 2. 初始环境变量加载(仅用于确定环境) self._load_environment(initial=True) # 3. 加载配置文件(根据环境) self._load_config_files() # 4. 完整环境变量加载(覆盖配置文件) self._load_environment() # 5. 解析变量引用 self._resolve_variables() logger.info("🔄 完成配置变量解析") # 6. 验证关键路径 self._validate_paths() # 7. 验证模型路径 self.validate_model_paths() except Exception as e: logger.critical(f"🔥 配置加载失败: {str(e)}", exc_info=True) raise RuntimeError(f"配置系统初始化失败: {str(e)}") from e # === 环境变量加载优化 === def _load_environment(self, initial=False): """加载环境变量(支持初始加载)""" # 初始加载时只加载基本环境变量 if initial: # 加载.env文件 env_file = self.base_dir / '.env' if env_file.exists(): try: load_dotenv(dotenv_path=str(env_file), override=True) logger.info(f"🌐 从 {env_file} 加载环境变量") except Exception as e: logger.error(f"❌ 加载环境变量失败: {str(e)}") return # 完整环境变量加载 for key, value in os.environ.items(): # 检查是否以环境前缀开头 if key.startswith(self.env_prefix + "_"): # 去掉前缀 config_key = key[len(self.env_prefix) + 1:] # 处理嵌套结构 if "__" in config_key: config_key = config_key.replace("__", ".") elif "_" in config_key and config_key.count("_") > 1: parts = config_key.split("_") config_key = ".".join(parts[:-1]) + "." + parts[-1] # 处理特殊值 if isinstance(value, str): if value.lower() in ['true', 'false']: value = value.lower() == 'true' elif value.isdigit(): value = int(value) elif value.replace('.', '', 1).isdigit() and value.count('.') < 2: try: value = float(value) except ValueError: pass # 保持字符串 # 设置配置值 self._set_nested_config(config_key, value) self._log_sensitive_value(key, value) elif key in self._config: # 处理非前缀的顶级配置 # 处理特殊值 if value.lower() in ['true', 'false']: value = value.lower() == 'true' elif value.isdigit(): value = int(value) elif value.replace('.', '', 1).isdigit() and value.count('.') < 2: try: value = float(value) except ValueError: pass # 保持字符串 self._config[key] = value logger.info(f"🔄 环境变量覆盖: {key}={self._mask_sensitive_value(key, value)}") # 路径自动修正 self._auto_correct_paths() def _auto_correct_paths(self): """自动修正所有路径配置""" for key_path in self.list_config_keys(): value = self.get(key_path) if value and isinstance(value, str): if any(path_key in key_path for path_key in self.path_keys): corrected = value.replace('\\', '/').rstrip('/') if corrected != value: self._set_nested_config(key_path, corrected) logger.info(f"🔄 自动修正路径: {key_path}={self._mask_sensitive_value(key_path, corrected)}") # === 配置访问增强 === def list_config_keys(self, prefix="", separator="."): """递归列出所有配置键""" keys = [] def explore(data, current_path): if isinstance(data, dict): for key, value in data.items(): new_path = f"{current_path}{separator}{key}" if current_path else key if isinstance(value, dict): explore(value, new_path) else: keys.append(new_path) explore(self._config, prefix) return keys def get(self, key_path, default=None, sep="."): """获取配置值(支持点分路径)""" keys = key_path.split(sep) value = self._config for key in keys: key = key.upper() if isinstance(value, dict) and key in value: value = value[key] else: return default return value def __getattr__(self, name): """允许通过属性方式访问配置项""" if name in self._config: return self._config[name] # 尝试访问嵌套配置 if '.' in name: return self.get(name) # 提供一些常用配置的默认值 if name == "DEFAULT_MODEL": return self.get("MODEL_PATHS.TEXT_BASE", "") # 记录警告而不是直接抛出异常 logger.warning(f"访问未定义的配置项: {name}") return None # === 配置持久化 === def save_config(self, filename="local.json"): """保存当前配置到文件""" config_dir = Path(self.get("CONFIG_DIR", self.base_dir / "config")) config_dir.mkdir(parents=True, exist_ok=True) config_file = config_dir / filename try: with open(config_file, 'w', encoding='utf-8') as f: json.dump(self._config, f, indent=2, ensure_ascii=False) logger.info(f"💾 配置已保存到 {config_file}") return True except Exception as e: logger.error(f"❌ 保存配置失败: {str(e)}") return False # === 默认配置 === def _set_defaults(self): """设置默认配置值(简化版)""" defaults = { "LOG_DIR": str(self.base_dir / "logs"), "CONFIG_DIR": str(self.base_dir / "config"), "MODEL_CACHE_DIR": str(self.base_dir / "model_cache"), "AGENT_NAME": "小蓝", "MODEL_BASE_PATH": "E:/AI_Models", "MODEL_PATHS": { "TEXT_BASE": "E:/AI_Models/Qwen2-7B", "TEXT_CHAT": "E:/AI_Models/deepseek-7b-chat", "MULTIMODAL": "E:/AI_Models/deepseek-vl2", }, "NETWORK": { "HOST": "0.0.0.0", "FLASK_PORT": 8000, }, "DIRECTORIES": { "PROJECT_ROOT": str(self.base_dir) } } self._config = defaults logger.debug("设置默认配置值") # === 测试工具 === def generate_sample_env(self): """生成示例环境文件""" content = [ "# AI_SYSTEM 环境变量配置", "# 注意:变量名使用双下划线表示嵌套路径", "", f"# 系统设置", f"{self.env_prefix}_AGENT_NAME=小蓝助手", f"{self.env_prefix}_MAX_WORKERS=8", "", f"# 数据库设置", f"{self.env_prefix}_DATABASE__DB_HOST=db.example.com", f"{self.env_prefix}_DATABASE__DB_PORT=5432", "", f"# 路径设置", f"{self.env_prefix}_MODEL_BASE_PATH=/opt/models", f"{self.env_prefix}_MODEL_PATHS__TEXT_BASE=/opt/models/qwen" ] return "\n".join(content) # === 其他方法保持不变 === # [其余方法如_load_config_files, _merge_config, validate_model_paths等保持不变] # 为节省空间,这里省略重复代码,实际实现中应保留完整 # 创建全局配置实例 config = CoreConfig.get_instance() # 测试代码 if __name__ == "__main__": # 设置详细日志 logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) print("=" * 50) print("配置系统增强版测试") print("=" * 50) # 获取配置实例 config = CoreConfig.get_instance() # 测试变量解析 config._config["TEST"] = { "PATH": "/home/${USER}", "DB_URL": "jdbc:postgresql://${DATABASE.DB_HOST}:${DATABASE.DB_PORT}/${DATABASE.DB_NAME}", "DEFAULT": "${NOT_EXIST|default_value}" } config._resolve_variables() print("\n变量解析测试结果:") print(f"PATH: {config.TEST['PATH']}") print(f"DB_URL: {config.TEST['DB_URL']}") print(f"DEFAULT: {config.TEST['DEFAULT']}") # 测试分页打印 long_text = "这是一个测试长文本\n" + "\n".join([f"行 {i}: 这是测试内容" * 20 for i in range(1, 51)]) print("\n分页打印测试(按Enter翻页):") config.print_long_text(long_text, lines_per_page=5) # 生成示例环境文件 print("\n示例环境文件内容:") print(config.generate_sample_env()) print("\n测试完成!")
最新发布
08-30
[root@node01 qxy27]# hdfs dfs -cat /flume/logs/beauty_logs/* SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. {"user_id": "98338449", "cat_id": "6513", "item_id": "327714861", "event_type": "浏览", "event_date": "2023-11-27", "event_hour": "15", "province": "上海市"} {"user_id": "85822276", "cat_id": "10961", "item_id": "148843860", "event_type": "浏览", "event_date": "2023-11-29", "event_hour": "13", "province": "浙江省"} {"user_id": "114305854", "cat_id": "5399", "item_id": "349592841", "event_type": "浏览", "event_date": "2023-11-26", "event_hour": "15", "province": "香港特别行政区"} {"user_id": "59627027", "cat_id": "1863", "item_id": "168035688", "event_type": "浏览", "event_date": "2023-12-16", "event_hour": "14", "province": "香港特别行政区"} {"user_id": "16511689", "cat_id": "8161", "item_id": "343652487", "event_type": "浏览", "event_date": "2023-12-11", "event_hour": "21", "province": "湖南省"} {"user_id": "16810079", "cat_id": "12326", "item_id": "84727656", "event_type": "浏览", "event_date": "2023-12-7", "event_hour": "14", "province": "云南省"} {"user_id": "98338449", "cat_id": "1121", "item_id": "74166174", "event_type": "浏览", "event_date": "2023-12-6", "event_hour": "10", "province": "湖北省"} {"user_id": "85822276", "cat_id": "10961", "item_id": "344896623", "event_type": "浏览", "event_date": "2023-11-29", "event_hour": "13", "province": "北京市"} {"user_id": "114305854", "cat_id": "11279", "item_id": "128323521", "event_type": "浏览", "event_date": "2023-11-26", "event_hour": "15", "province": "新疆维吾尔自治区"} {"user_id": "59627027", "cat_id": "10533", "item_id": "249603098", "event_type": "浏览", "event_date": "2023-11-22", "event_hour": "09", "province": "新疆维吾尔自治区"} 只有一点数据
06-08
### 确认 HDFS 目录是否持续接收到新数据 #### 检查 HDFS 中的文件更新状态 可以通过以下命令实时查看 HDFS 目录 `/flume/logs/beauty_logs` 中的文件变化情况: ```bash hadoop fs -ls /flume/logs/beauty_logs ``` 此命令会列出该目录下的所有文件及其最后修改时间。如果 Flume 正在向 HDFS 写入数据,则会看到新的文件不断生成,或者现有文件的大小在不断增加[^3]。 #### 使用 `tail` 命令监控日志 为了更直观地观察文件内容的变化,可以使用以下命令查看最新文件的内容更新: ```bash hadoop fs -tail /flume/logs/beauty_logs/FlumeData.* ``` 此命令会显示指定文件的最后部分数据,并在文件追加时动态更新输出[^2]。 #### 验证 Flume 日志中的写入记录 Flume 的日志文件中会记录每次写入 HDFS 的操作详情。通过以下命令实时查看 Flume 的日志文件: ```bash tail -f /path/to/flume/logs/flume.log ``` 在日志中查找类似以下的消息,确认 Flume 是否正在将数据写入 HDFS: - 创建临时文件:`Creating hdfs://node01:9000/flume/logs/beauty_logs/FlumeData.1749286892111.tmp`[^4] - 文件重命名:`Renaming hdfs://node01:9000/flume/logs/beauty_logs/FlumeData.1749286892111.tmp to hdfs://node01:9000/flume/logs/beauty_logs/FlumeData.1749286892111`[^4] #### 解决 SLF4J 日志绑定问题 SLF4J 是一个日志门面库,用于统一管理 Java 应用程序的日志输出。如果出现 SLF4J 绑定问题,通常是因为类路径中存在多个日志实现或版本冲突。解决方法如下: 1. **检查依赖冲突**:确保 Flume 的 `lib` 目录中只有一个 SLF4J 实现库(如 `slf4j-log4j12.jar` 或 `logback-classic.jar`)。如果存在多个实现库,移除多余的。 2. **验证日志配置**:确认 Flume 的日志配置文件(如 `log4j.properties`)正确设置。例如: ```properties log4j.rootLogger=INFO, console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n ``` #### 验证网络和 HDFS 状态 确保 Flume 能够正常访问 HDFS 集群。可以通过以下命令测试 HDFS 的连通性: ```bash hadoop fs -mkdir /test hadoop fs -put local_file.txt /test/ ``` 如果上述命令执行成功,则说明 HDFS 可用[^1]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值