将Logger的数据导入到数据库详解

本文介绍了如何利用LOG4J的JDBCAppender功能,将日志信息直接存入数据库,包括创建日志表、配置LOG4J的properties文件,以及设置SQL插入语句,实现日志的数据库持久化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天公司说要在系统中添加日志查询统计功能,刚开始第一个念头就是用IO流读取日志文件,然后持久化,最后在系统中查询。结果看了一些LOG4J的说明文档后发现,原来LOG4J本身就有将日志信息存入数据库功能,这样就大大节省了开发的时间。现在把方法记录下来,希望对有需要的朋友有所帮助。
一、前提条件
      系统必须是使用LOG4J进行日志管理,否则方法无效。
      系统必须包含commons-logging-xxx.jar,log4j-xxx.jar这两个JAR包,XXX为版本号。
二、操作步骤
      1、创建日志表
           要把日志持久化,必须在数据库中创建一张用来存储日志信息的表,表内字段为日志的一个主要属性包括:操作类,执行方法,打印时间,日志级别,日志内容。
           CREATE TABLE RESLOG (
LOGID VARCHAR2(20) NOT NULL,
                                                 CLASS VARCHAR2(200),
                                                 METHOD VARCHAR2(100),
                                                 CREATETIME DATE,
                                                 LOGLEVEL VARCHAR2(50),
                                                 MSG VARCHAR2(4000)
         )
2、日志管理配置
            LOG4J主要有两种配置文件.properties和.xml,这里以properties文件为基础来讲述,关于XML文件的配置,相信大家看完下面的介绍也一样能轻松完成。
            通常在LOG4J.PROPERTIES文件的第一行是:
            log4j.rootLogger= XXX,这句是控制日志的输出,如果想把日志输出到数据库,则需要在XXX中添加“DB”,如     log4j.rootLogger=INFO,stdout,Platform,db。
   上面这句就是把日志中级别为INFO的信息输出到STDOUT,PLATFORM和DB(DATABASE)中。
            配置好如上的信息,LOG4J就知道用户是想把信息存入数据库,接下来我们就要来配置数据库的相关信息(包括缓存,数据库连接信息,和执行SQL),配置信息如下:


            ###JDBCAppender
            log4j.appender.db = org.apache.log4j.jdbc.JDBCAppender
            //这个配置是选择使用JDBCAppender方法,将日志信息存储到数据库。当然,如果你还要做其他操作,可以自己写个类,继承JDBCAppender就OK了。
            log4j.appender.db.BufferSize=1
            //这个配置是告诉LOG4J,有多少条日志信息后才存入数据库,我这里是1,就是说有一条就查一条,显然这样在生产环境下是很影响系统性能的。
            log4j.appender.db.driver=oracle.jdbc.driver.OracleDriver
            //这个配置是告诉LOG4J,做数据库存储所用的驱动。
            log4j.appender.db.URL=jdbc:oracle:thin:@<ip>:<port>:<sid>
            //这个配置数据库连接的URL,不用说也都知道。
            log4j.appender.db.user=XXX
            log4j.appender.db.password=XXX
            //上面两个是数据库连接时的用户名和密码
            log4j.appender.db.sql=insert into RESLOG
                  (LogId,Class,Method,createTime,LogLevel,MSG)
                  values (SQ_RESLOG_LOGID.Nextval,'%C','%M',
                  to_date('%d{yyyy-MM-dd HH:mm:ss}','yyyy-MM-dd
                  HH24:mi:ss'),'%p','%m')
            //这个配置是告诉当LOG4J吧日志存储数据库时用的SQL语句。SQ_RESLOG_LOGID.Nextval是我建的一个SEQUENCE;‘%C’是日志中的CLASS;‘%M’是打印日志是执行到类里的方法;‘%d’是打印的时间,它支持格式化;‘%P’是日志级别,包括INFO、DEBUG、ERROR等;‘%m’是MSG,日志内容。注意这里的参数区分大小写。
            log4j.appender.db.layout=org.apache.log4j.PatternLayout
 
        通过上面的配置,现在再启动服务,LOG4J就会自动把原来存储在.LOG文件中的信息,同时存储到数据库了。
        当然,LOG4J还支持自定义参数,如打印日志时当前的用户信息等,这个以后以机会再说。






完整的配置文件内容如下:
 log4j.rootLogger=INFO,stdout,db   


  #\u63a7\u5236\u53f0
  log4j.logger.stdout=INFO,stdout
  log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  # Pattern to output the caller's file name and line number.
  log4j.appender.stdout.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n


  #\u8c03\u8bd5
  log4j.logger.D=DEBUG,D
  log4j.appender.D=org.apache.log4j.RollingFileAppender
  log4j.appender.D.File=../logs/mmdb-debug.log
  log4j.appender.D.MaxFileSize= 100KB
  log4j.appender.D.MaxBackupIndex=1
  log4j.appender.D.layout=org.apache.log4j.PatternLayout
  log4j.appender.D.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
  
  #\u4fe1\u606f
  log4j.logger.I=INFO,I
  log4j.appender.I=org.apache.log4j.RollingFileAppender
  log4j.appender.I.File=../logs/mmdb-info.log
  log4j.appender.I.MaxFileSize= 100KB
  log4j.appender.I.MaxBackupIndex=1
  log4j.appender.I.layout=org.apache.log4j.PatternLayout
  log4j.appender.I.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
  
  #\u5f02\u5e38
  log4j.logger.E=ERROR,E
  log4j.appender.E=org.apache.log4j.RollingFileAppender
  log4j.appender.E.File=../logs/mmdb-error.log
  log4j.appender.E.MaxFileSize= 100KB
  log4j.appender.E.MaxBackupIndex=1
  log4j.appender.E.layout=org.apache.log4j.PatternLayout
  log4j.appender.E.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} [%c]-[%p] %m%n
  
  
  
    
 log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender
 log4j.appender.db.File=../Applog/mmdblog/mmdb-debug.log
 log4j.appender.db.MaxFileSize= 100KB
 log4j.appender.db.MaxBackupIndex=1
 log4j.appender.db.layout=org.apache.log4j.PatternLayout
 log4j.appender.db.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
 log4j.appender.db.driver=com.mysql.jdbc.Driver
 log4j.appender.db.user=root
 log4j.appender.db.password=root
 log4j.appender.db.URL=jdbc:mysql://localhost:3306/uinnv
 log4j.appender.db.sql=INSERT INTO         startstopinfo(msg,time,username,app,operation,object,result,detail)VALUES('%X{msg}','%d{yyyy-MM-dd   HH\:mm\:ss}','%X{username}','%X{app}','%X{operation}','%X{object}','%X{result}','%X{detail}')






       
<think>由于问题涉及Odoo 14的二次开发,我们需要编写一个模块来实现导入Excel并写入指定模型的功能。这里主要分为几个步骤: 1. 创建模型(如果已有模型mrp.workcenter.productivity,则无需创建,但可能需要添加字段) 2. 创建视图(添加一个导入按钮和可能的列表视图) 3. 编写一个向导(用于上传Excel文件) 4. 解析Excel文件(使用第三方库如xlrd或openpyxl) 5. 将解析后的数据写入模型 注意:Odoo本身支持导入CSV,但这里要求Excel,所以需要自定义。 步骤详解: 1. 安装依赖:我们需要一个库来读取Excel文件。Odoo默认没有安装,所以需要在模块的__manifest__.py中声明依赖,或者让用户手动安装。这里我们使用xlrd(注意:xlrd 2.0以上版本不支持xlsx,所以可能需要使用openpyxl)。考虑到xlsx的流行,我们使用openpyxl。 在__manifest__.py中添加: 'depends': ['base', 'mrp'], 'external_dependencies': { 'python': ['openpyxl'], }, 2. 创建向导模型(用于上传文件): - 创建一个模型(如import.excel.wizard)继承TransientModel。 - 包含一个Binary类型的字段用于上传文件。 - 包含一个按钮,点击后触发解析和导入操作。 3. 在向导的按钮方法中: - 读取二进制文件内容,使用openpyxl加载工作簿。 - 遍历工作表,读取数据(假设第一行是标题,第二行开始是数据)。 - 根据标题映射到模型的字段(可以硬编码映射,也可以灵活配置,这里为了简单,我们假设Excel列的顺序和字段的对应关系)。 - 创建mrp.workcenter.productivity记录。 4. 视图:为向导创建一个表单视图,包含文件上传字段和按钮。 具体实现: 1. 创建模块结构(此处省略模块创建步骤,假设模块名为excel_import_mrp) 2. 创建向导模型:models/import_excel.py ```python # -*- coding: utf-8 -*- from odoo import models, fields, api import base64 import logging from io import BytesIO from openpyxl import load_workbook _logger = logging.getLogger(__name__) class ImportExcelWizard(models.TransientModel): _name = 'import.excel.wizard' _description = 'Wizard to import Excel file for Workcenter Productivity' excel_file = fields.Binary(string='Excel File', required=True) file_name = fields.Char(string='Filename') def import_excel(self): self.ensure_one() # 读取二进制数据 if self.excel_file: # 解码base64 excel_data = base64.b64decode(self.excel_file) # 使用BytesIO换为文件流 wb = load_workbook(filename=BytesIO(excel_data), read_only=True) # 获取第一个工作表 ws = wb.active # 获取标题行(第一行) headers = [] for col in range(1, ws.max_column+1): cell_value = ws.cell(row=1, column=col).value headers.append(cell_value.strip() if cell_value else '') # 从第二行开始读取数据 WorkcenterProductivity = self.env['mrp.workcenter.productivity'] for row in range(2, ws.max_row+1): row_data = {} for col, header in enumerate(headers, 1): cell_value = ws.cell(row=row, column=col).value row_data[header] = cell_value # 将row_data映射到模型字段,这里假设Excel列名与模型字段名一致(注意:实际可能不一致,需要调整) # 注意:模型中的字段名可能与Excel列名不完全相同,这里需要根据实际情况换 # 例如,Excel列名为'workcenter_id',模型字段也是'workcenter_id',但注意可能为外键,需要处理 # 这里我们假设Excel中直接提供的是ID,或者通过某种方式换为ID(实际中可能需要根据名称查找) vals = {} for key, value in row_data.items(): # 根据模型字段类型可能需要特殊处理 # 例如,日期字段:如果Excel中是日期字符串,需要换为Odoo日期格式 # 这里简单处理:直接赋值(如果字段名一致,且值类型匹配) vals[key] = value # 创建记录,注意:如果字段有必填项,确保vals中包含所有必填字段 try: WorkcenterProductivity.create(vals) except Exception as e: _logger.error("Error creating record from Excel: %s", e) # 关闭工作簿 wb.close() return {'type': 'ir.actions.act_window_close'} ``` 3. 向导视图:views/import_excel_wizard_view.xml ```xml <?xml version="1.0" encoding="utf-8"?> <odoo> <record id="import_excel_wizard_view_form" model="ir.ui.view"> <field name="name">import.excel.wizard.form</field> <field name="model">import.excel.wizard</field> <field name="arch" type="xml"> <form string="Import Excel"> <group> <field name="file_name"/> <field name="excel_file" string="Excel File" filename="file_name" required="1"/> </group> <footer> <button name="import_excel" string="Import" type="object" class="btn-primary"/> <button string="Cancel" class="btn-secondary" special="cancel"/> </footer> </form> </field> </record> <!-- 动作打开向导 --> <record id="action_import_excel_wizard" model="ir.actions.act_window"> <field name="name">Import Excel</field> <field name="res_model">import.excel.wizard</field> <field name="view_mode">form</field> <field name="target">new</field> </record> </odoo> ``` 4. 在mrp.workcenter.productivity的列表视图中添加一个按钮,用于打开向导: 在views/mrp_workcenter_productivity_views.xml中(需要继承原有的视图): ```xml <?xml version="1.0" encoding="utf-8"?> <odoo> <!-- 在mrp.workcenter.productivity的列表视图中添加一个按钮 --> <record id="view_mrp_workcenter_productivity_tree_inherit" model="ir.ui.view"> <field name="name">mrp.workcenter.productivity.tree.inherit</field> <field name="model">mrp.workcenter.productivity</field> <field name="inherit_id" ref="mrp.mrp_workcenter_productivity_tree_view"/> <field name="arch" type="xml"> <xpath expr="//tree" position="before"> <header> <button name="%(action_import_excel_wizard)d" string="Import Excel" type="action" class="oe_highlight"/> </header> </xpath> </field> </record> </odoo> ``` 5. 在__manifest__.py中声明文件和视图: ```python { 'name': 'MRP Workcenter Productivity Excel Import', 'version': '1.0', 'summary': 'Import Excel data to MRP Workcenter Productivity', 'category': 'Manufacturing', 'author': 'Your Name', 'website': 'https://www.yourcompany.com', 'depends': ['base', 'mrp'], 'external_dependencies': { 'python': ['openpyxl'], }, 'data': [ 'security/ir.model.access.csv', 'views/import_excel_wizard_view.xml', 'views/mrp_workcenter_productivity_views.xml', ], 'installable': True, 'application': False, } ``` 6. 注意:在模型字段映射时,上述代码假设Excel列名和模型字段名完全一致。如果不一致,需要建立映射关系。例如,可以创建一个映射字典: ```python # 在import_excel方法内部,读取数据后 field_mapping = { 'Excel列名1': '模型字段1', 'Excel列名2': '模型字段2', # ... } vals = {} for key, value in row_data.items(): if key in field_mapping: model_field = field_mapping[key] vals[model_field] = value ``` 7. 对于外键字段,不能直接赋值ID(如果Excel中给的是名称,则需要查找相关模型获取ID)。例如,workcenter_id在Excel中是工作中心名称,则需要先在工作中心模型中查找: ```python if model_field == 'workcenter_id': workcenter = self.env['mrp.workcenter'].search([('name', '=', value)], limit=1) if workcenter: vals[model_field] = workcenter.id else: # 处理未找到的情况 _logger.warning("Workcenter %s not found", value) ``` 8. 同样,日期时间字段可能需要换:如果Excel中是日期字符串,需要使用fields.Datetime或fields.Date来换。 以上是一个基本的实现框架。实际应用中可能需要更完善的错误处理和日志记录,以及更灵活的数据映射。 注意:由于mrp.workcenter.productivity模型可能有一些必填字段和约束,在创建记录时需要确保这些字段在Excel中提供或通过其他方式赋值。 最后,在模块安装后,用户可以在mrp.workcenter.productivity的列表视图中点击“Import Excel”按钮,上传Excel文件并导入数据。 §§相关问题§§: 1. 如何为Odoo模块添加外部Python依赖? 2. 在Odoo中,如何为已有模型添加自定义按钮并打开向导? 3. 在导入Excel时,如何处理日期格式的列? 4. 如果Excel文件很大,如何优化导入性能以避免内存问题?</think>以下是 Odoo 14 中实现 Excel 导入并写入 `mrp.workcenter.productivity` 模型的完整解决方案: ### 解决方案步骤: 1. **创建自定义模块** 2. **添加文件上传向导** 3. **实现 Excel 解析逻辑** 4. **写入目标模型** 5. **添加菜单和权限** --- ### 1. 模块文件结构 ``` excel_import_mrp/ ├── __init__.py ├── __manifest__.py ├── models/ │ ├── __init__.py │ ├── excel_import.py # 向导模型 │ └── mrp_models.py # 模型扩展 ├── views/ │ └── templates.xml # 视图定义 └── wizard/ └── import_wizard.xml # 向导视图 ``` --- ### 2. 代码实现 #### 2.1 `__manifest__.py` ```python { 'name': 'MRP Workcenter Productivity Excel Import', 'version': '1.0', 'summary': 'Import Excel data to Workcenter Productivity', 'category': 'Manufacturing', 'depends': ['base', 'mrp'], 'data': [ 'security/ir.model.access.csv', 'views/templates.xml', 'wizard/import_wizard.xml', ], 'external_dependencies': { 'python': ['openpyxl'], }, 'installable': True, 'application': True, } ``` #### 2.2 `models/excel_import.py` (向导模型) ```python from odoo import models, fields, api import base64 import logging from io import BytesIO from openpyxl import load_workbook _logger = logging.getLogger(__name__) class ExcelImportWizard(models.TransientModel): _name = 'excel.import.wizard' _description = 'Excel Import Wizard' excel_file = fields.Binary(string='Excel File', required=True) file_name = fields.Char(string='Filename') def action_import(self): self.ensure_one() # 1. 解码Excel文件 excel_data = base64.b64decode(self.excel_file) workbook = load_workbook(filename=BytesIO(excel_data), data_only=True) sheet = workbook.active # 2. 准备数据容器 records = [] # 3. 遍历行 (从第2行开始,跳过标题) for row in sheet.iter_rows(min_row=2, values_only=True): # 示例列顺序: [日期, 工单, 工作中心, 操作员, 产出数量] if not row[0]: # 跳过空行 continue records.append({ 'date_start': row[0], # 日期 'workorder_id': self._get_workorder_id(row[1]), # 工单 'workcenter_id': self._get_workcenter_id(row[2]), # 工作中心 'user_id': self._get_user_id(row[3]), # 操作员 'qty_produced': row[4], # 产出数量 }) # 4. 批量创建记录 if records: self.env['mrp.workcenter.productivity'].create(records) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': '导入成功', 'message': f'已导入 {len(records)} 条记录', 'sticky': False, } } def _get_workorder_id(self, name): """ 通过名称查找工单ID """ return self.env['mrp.workorder'].search([('name', '=', name)], limit=1).id def _get_workcenter_id(self, name): """ 通过名称查找工作中心ID """ return self.env['mrp.workcenter'].search([('name', '=', name)], limit=1).id def _get_user_id(self, login): """ 通过登录名查找用户ID """ return self.env['res.users'].search([('login', '=', login)], limit=1).id ``` #### 2.3 `views/templates.xml` (添加菜单) ```xml <odoo> <!-- 添加菜单入口 --> <menuitem id="menu_mrp_excel_import" name="Excel导入" parent="mrp.menu_mrp_root" sequence="10"/> <!-- 添加动作 --> <record id="action_excel_import_wizard" model="ir.actions.act_window"> <field name="name">导入生产数据</field> <field name="res_model">excel.import.wizard</field> <field name="view_mode">form</field> <field name="target">new</field> </record> <!-- 将动作绑定到菜单 --> <menuitem id="submenu_excel_import" name="导入Excel" parent="menu_mrp_excel_import" action="action_excel_import_wizard"/> </odoo> ``` #### 2.4 `wizard/import_wizard.xml` (向导视图) ```xml <odoo> <record id="view_excel_import_wizard_form" model="ir.ui.view"> <field name="name">excel.import.wizard.form</field> <field name="model">excel.import.wizard</field> <field name="arch" type="xml"> <form string="导入Excel"> <sheet> <group> <label string="请选择Excel文件(格式要求:第一行为标题行)" colspan="4"/> <group> <field name="file_name" filename="excel_file"/> <field name="excel_file" required="1"/> </group> </group> </sheet> <footer> <button name="action_import" string="导入" type="object" class="btn-primary"/> <button string="取消" class="btn-secondary" special="cancel"/> </footer> </form> </field> </record> </odoo> ``` #### 2.5 `security/ir.model.access.csv` ``` id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_excel_import_wizard,excel.import.wizard,model_excel_import_wizard,base.group_user,1,1,1,1 ``` --- ### 3. 关键功能说明 1. **Excel解析流程**: - 使用 `openpyxl` 读取二进制Excel数据 - 跳过标题行(第1行) - 按列映射到 `mrp.workcenter.productivity` 字段 - 自动处理外键关联(通过名称查找ID) 2. **字段映射关系**: ```python { 'date_start': 'A', # 日期时间 'workorder_id': 'B', # 工单名称 'workcenter_id': 'C', # 工作中心名称 'user_id': 'D', # 操作员登录名 'qty_produced': 'E' # 产出数量 } ``` 3. **错误处理**: - 自动跳过空行 - 外键查找失败时返回False(Odoo会创建失败记录) - 使用日志记录错误 (`_logger.error`) --- ### 4. 使用说明 1. **安装模块**: ```bash pip install openpyxl odoo-bin -i excel_import_mrp ``` 2. **Excel格式要求**: ``` | 日期 | 工单编号 | 工作中心 | 操作员 | 产出数量 | |------------|----------|----------|----------|----------| | 2023-01-01 | WO001 | CNC-01 | user1 | 100 | | 2023-01-02 | WO002 | ASSY-01 | user2 | 50 | ``` 3. **操作路径**: 制造 → Excel导入 → 导入Excel → 上传文件 → 点击"导入" --- ### 5. 注意事项 1. **性能优化**: - 使用 `create()` 批量操作而非单条创建 - 添加 `limit=1` 提高搜索效率 - 处理1000+行数据时建议分页处理 2. **安全考虑**: - 文件类型验证(可添加内容类型检查) - 设置最大文件大小限制(建议在视图层实现) 3. **扩展方向**: - 添加字段映射配置界面 - 支持多Sheet处理 - 添加导入模板下载功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值