当前位置: 首页 > news >正文

达梦JNI方式调用Logmnr接口调用示例

介绍

1) Logmnr 包是达梦数据库的日志分析工具,达梦提供了 JNI 接口和 C 接口,供应用程序直接调用。

2) 在使用 Logmnr 包中的接口之前,需要先开启两个日志相关参数:一是开启归档(设置 INI 参数 ARCH_INI 为 1);二是开启在日志中记录逻辑操作的功能(设置 INI 参数 RLOG_APPEND_LOGIC 为 1、2 或者 3)。

3) 本文重点JNI的方式解析数据库的归档日志,logmnr.jar包是达梦数据库的日志分析工具的JNI 接口,通过这个jar包解析归档日志。

4)在DM8程序员手册中,提供示例:https://eco.dameng.com/document/dm/zh-cn/pm/logmnr-interface-instructions.html#9.1.2%20%E7%BC%96%E7%A8%8B%E5%AE%9E%E4%BE%8B

编写用例代码

因手册中提供的示例比较简单,现对这块示例做一些补充,

1)实现解析指定目录下所有的归档文件同时将结果按不同文件保存,

2)说明linux跟window系统环境下运行方式。

3)示例中不再是解析100条数据写入文本,而是将这个归档所有的结果集写入到文本

4)为便于测试,将代码中的硬编码变量调整为传参形式

目录结构如下,包含 Java 源码、依赖库、日志归档文件等子目录和文件:

├── LogmnrTest.java # 主程序 Java 源文件

├── result/ # 程序运行的输出结果目录

├── lib/ # Java 依赖的 Jar 包目录

│ └── com.dameng.logmnr.jar # 达梦 LogMnr 接口 Jar 包

├── arch_file/ # 存放归档日志的目录(LogMnr 分析目标)

│ └── ARCHIVE_LOCAL_0x4B8158F0_EP0_2025-05-26_16-04-49.log


import com.dameng.logmnr.LogmnrDll;
import com.dameng.logmnr.LogmnrRecord;import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;/*** 日志分析工具* 使用方法: java LogmnrTest <数据库IP> <数据库端口> <数据库用户名> <数据库密码> [日志目录路径]* 示例: java LogmnrTest 192.168.112.168 5236 SYSDBA SYSDBA* 示例: java LogmnrTest 192.168.112.168 5236 SYSDBA SYSDBA D:\dmdbms\arch_file* 注意: 如果不指定日志目录路径,将使用程序同级目录下的arch_file目录*/
public class LogmnrTest
{private static void printUsage() {System.out.println("使用方法: java LogmnrTest <数据库IP> <数据库端口> <数据库用户名> <数据库密码> [日志目录路径]");System.out.println("示例: java LogmnrTest 192.168.112.168 5236 SYSDBA SYSDBA");System.out.println("示例: java LogmnrTest 192.168.112.168 5236 SYSDBA SYSDBA D:\\dmdbms\\arch_file");System.out.println("注意: 如果不指定日志目录路径,将使用程序同级目录下的arch_file目录");}private static String getDefaultLogDirectory() {// 获取程序运行目录String currentDir = System.getProperty("user.dir");return currentDir + File.separator + "arch_file";}private static List<File> getLogFiles(String directoryPath) {List<File> logFiles = new ArrayList<>();File directory = new File(directoryPath);if (!directory.exists() || !directory.isDirectory()) {System.err.println("错误: 目录不存在或不是有效目录: " + directoryPath);return logFiles;}File[] files = directory.listFiles();if (files != null) {for (File file : files) {if (file.isDirectory()) {logFiles.addAll(getLogFiles(file.getAbsolutePath()));} else if (file.getName().toLowerCase().endsWith(".log")) {logFiles.add(file);}}}return logFiles;}private static void processLogFile(long connid, File logFile, String resultDir) throws Exception {LogmnrDll.addLogFile(connid, logFile.getAbsolutePath(), 3);LogmnrDll.startLogmnr(connid, -1, null, null);// 创建结果文件String resultFileName = logFile.getName().replace(".log", "_result.txt");File resultFile = new File(resultDir, resultFileName);PrintStream filePs = new PrintStream(resultFile);PrintStream consolePs = System.out; // 保存控制台输出流System.out.println("开始处理日志文件:" + logFile.getName());LogmnrRecord[] records;int batchSize = 100;int totalCount = 0;int recordIndex = 0;// 循环获取数据并立即写入文件while (true) {records = LogmnrDll.getData(connid, batchSize);if (records == null || records.length == 0) {break;}// 处理当前批次的数据for (LogmnrRecord record : records) {// 写入文件filePs.println("-----------------------------" + recordIndex + "-----------------------------" + "\n");filePs.println("xid:" + record.getXid() + "\n");filePs.println("operation:" + record.getOperation() + "\n");filePs.println("sqlRedo:" + record.getSqlRedo() + "\n");filePs.println("#############################" + recordIndex + "#############################" + "\n");filePs.println("scn:" + record.getScn() + "\n");filePs.println("startScn:" + record.getStartScn() + "\n");filePs.println("commitScn:" + record.getCommitScn() + "\n");filePs.println("timestamp:" + record.getTimestamp() + "\n");filePs.println("startTimestamp:" + record.getStartTimestamp() + "\n");filePs.println("commitTimestamp:" + record.getCommitTimestamp() + "\n");filePs.println("operationCode:" + record.getOperationCode() + "\n");filePs.println("rollBack:" + record.getRollBack() + "\n");filePs.println("segOwner:" + record.getSegOwner() + "\n");filePs.println("tableName:" + record.getTableName() + "\n");filePs.println("rowId:" + record.getRowId() + "\n");filePs.println("rbasqn:" + record.getRbasqn() + "\n");filePs.println("rbablk:" + record.getRbablk() + "\n");filePs.println("rbabyte:" + record.getRbabyte() + "\n");filePs.println("dataObj:" + record.getDataObj() + "\n");filePs.println("dataObjv:" + record.getDataObjv() + "\n");filePs.println("rsId:" + record.getRsId() + "\n");filePs.println("ssn:" + record.getSsn() + "\n");filePs.println("csf:" + record.getCsf() + "\n");filePs.println("status:" + record.getStatus() + "\n");filePs.println("#############################" + recordIndex + "#############################" + "\n");recordIndex++;}totalCount += records.length;// 进度信息同时输出到控制台和文件String progressMsg = "已处理 " + totalCount + " 条记录...";consolePs.println(progressMsg);filePs.println(progressMsg);filePs.flush(); // 确保数据及时写入文件}String completionMsg = "总共处理 " + totalCount + " 条记录";consolePs.println(completionMsg);filePs.println(completionMsg);consolePs.println("结果已保存到文件:" + resultFile.getAbsolutePath());filePs.flush();filePs.close();LogmnrDll.endLogmnr(connid, 1);}public static void main(String[] args) {// 检查参数数量if (args.length < 4 || args.length > 5) {System.err.println("错误: 参数数量不正确");printUsage();return;}try {// 解析参数String dbIp = args[0];int dbPort = Integer.parseInt(args[1]);String dbUser = args[2];String dbPassword = args[3];// 获取日志目录路径String logDirectory;if (args.length == 5) {logDirectory = args[4];} else {logDirectory = getDefaultLogDirectory();System.out.println("使用默认日志目录: " + logDirectory);}// 创建结果目录String resultDir = "result";File resultDirectory = new File(resultDir);if (!resultDirectory.exists()) {resultDirectory.mkdir();}LogmnrDll.initLogmnr();long connid = LogmnrDll.createConnect(dbIp, dbPort, dbUser, dbPassword);// 获取并处理日志文件List<File> logFiles = getLogFiles(logDirectory);if (logFiles.isEmpty()) {System.err.println("错误: 在指定目录下未找到日志文件: " + logDirectory);return;}System.out.println("找到 " + logFiles.size() + " 个日志文件");// 处理每个日志文件for (File logFile : logFiles) {try {processLogFile(connid, logFile, resultDir);} catch (Exception e) {System.err.println("处理文件 " + logFile.getName() + " 时发生错误: " + e.getMessage());e.printStackTrace();}}LogmnrDll.deinitLogmnr();} catch (NumberFormatException e) {System.err.println("错误: 数据库端口必须是数字");printUsage();} catch (Exception e) {System.err.println("错误: " + e.getMessage());e.printStackTrace();}}
}

window下运行

window跟linux环境下配置方式基本一致,因Logmnr接口底层走的是C接口,上面的目录结构及官方示例可以看出来 连接数据库时走的不是jdbc接口而是DM的dpi接口。

第一种方式 :window环境下需要配置dpi库跟Logmnr库的环境变量,注:较新的DM版本已将dpi依赖库独立到 dmdbms\drivers\dpi下这样可以不添加bin目录。

第二种方式 :程序运行前在启动类的VM options参数 配置DLL的路径

DM调用的dll在达梦安装目录的三个目录里面都需要引入

-Djava.library.path=D:\dmdbms\bin;D:\dmdbms\drivers_1_4_80\logmnr; -Dfile.encoding=UTF-8

注:项目中com.dameng.floader.jar 跟 com.dameng.logmnr.jar 不能同时使用,函数冲突后会报下面的这样的错。

Exception in thread "main" java.lang.NoSuchMethodError: com.dameng.JNIUtil.loadDmLibrarys(Ljava/lang/String;)Vat com.dameng.logmnr.LogmnrDll.<clinit>(LogmnrDll.java:18)at com.dameng.LogmnrTest.main(LogmnrTest.java:16)

将需要解析的归档放到同级的arch_file目录下,或者指定参数的时候指定要解析的归档目录,随后启动程序。

使用方法: java LogmnrTest <数据库IP> <数据库端口> <数据库用户名> <数据库密码> [日志目录路径]

: 如果不指定日志目录路径,将使用程序同级目录下的arch_file目录示例: java LogmnrTest 192.168.112.168 5236 SYSDBA SYSDBA
示例: java LogmnrTest 192.168.112.168 5236 SYSDBA SYSDBA D:\dmdbms\arch_file

程序运行后,会在同级目录下的result下生成对应的解析归档后的数据。

linux编译运行

linux跟window的运行方式是一样的,配置环境变量后 指定参数运行程序即可。

1)目录结构如下 同时将要解析的归档放到arch_file目录下

[root@localhost LogmnrTest]# pwd
/opt/LogmnrTest
# 目录结构
├── LogmnrTest.java           # 主程序 Java 源文件
├── result/                    # 程序运行的输出结果目录
├── lib/                      # Java 依赖的 Jar 包目录
│   └──  com.dameng.logmnr.jar        # 达梦 LogMnr 接口 Jar 包
├── arch_file/                # 存放归档日志的目录(LogMnr 分析目标)
│   └── ARCHIVE_LOCAL_0x4B8158F0_EP0_2025-05-26_16-04-49.log

2)临时指定当前用户的环境变量

[root@localhost LogmnrTest]# export LD_LIBRARY_PATH=/opt/dmdbms/bin:/opt/dmdbms/drivers/logmnr:$LD_LIBRARY_PATH

3)编译并运行java文件

[root@localhost LogmnrTest]# javac -cp "lib/*" LogmnrTest.java
[root@localhost LogmnrTest]# java -cp ".:lib/*" -Djava.library.path=/opt/dmdbms/bin:/opt/dmdbms/drivers/logmnr LogmnrTest 192.168.112.168 5236 SYSDBA SYSDBA

程序运行后会有对应的提示。

附录

no zlib in java.library.path

程序时报错:no zlib in java.library.path,

Exception in thread “main” java.lang.UnsatisfiedLinkError: no zlib in java.library.path

at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1864)at java.lang.Runtime.loadLibrary0(Runtime.java:870)at java.lang.System.loadLibrary(System.java:1122)at com.dameng.JNIUtil.loadLibrary(JNIUtil.java:210)at com.dameng.logmnr.LogmnrDll.<clinit>(LogmnrDll.java:17)at com.dameng.App.main(App.java:18)

解决方案:zlib库在dmdbms/bin目录下有个zlib.dll,可以将这个库拷贝到drivers\logmnr路径下,或者将dmdbms/bin添加到环境变量下

no libeay32 in java.library.path

程序时报错:no libeay32 in java.library.path

Exception in thread "main" java.lang.UnsatisfiedLinkError: no libeay32 in java.library.pathat java.lang.ClassLoader.loadLibrary(ClassLoader.java:1864)at java.lang.Runtime.loadLibrary0(Runtime.java:870)at java.lang.System.loadLibrary(System.java:1122)at com.dameng.JNIUtil.loadLibrary(JNIUtil.java:210)at com.dameng.JNIUtil.loadSSLLibrary(JNIUtil.java:184)at com.dameng.logmnr.LogmnrDll.<clinit>(LogmnrDll.java:16)at com.dameng.App.main(App.java:18)

解决方案:可以换个较新的driver版本logmnr\dependencies下有libeay32.dll等依赖库 或者将dmdbms/bin添加到环境变量下

java.lang.UnsatisfiedLinkError: /opt/dmdbms/bin/libdmlogmnr_client.so: libdmdpi.so: 无法打开共享对象文件: 没有那个文件或目录

Exception in thread "main" java.lang.UnsatisfiedLinkError: /opt/dmdbms/bin/libdmlogmnr_client.so: libdmdpi.so: 无法打开共享对象文件: 没有那个文件或目录at java.lang.ClassLoader$NativeLibrary.load(Native Method)at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1934)at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1850)at java.lang.Runtime.loadLibrary0(Runtime.java:871)at java.lang.System.loadLibrary(System.java:1124)at com.dameng.JNIUtil.loadLibrary(JNIUtil.java:210)at com.dameng.JNIUtil.loadDmLibrarys(JNIUtil.java:261)at com.dameng.JNIUtil.loadDmLibrarys(JNIUtil.java:228)at com.dameng.logmnr.LogmnrDll.<clinit>(LogmnrDll.java:19)at LogmnrTest.main(LogmnrTest.java:17)
# 说明 Java 已经成功找到了 libdmlogmnr_client.so,但这个 .so 又依赖另一个动态库 libdmdpi.so,系统没找到这个依赖库。

问题说明:Java 已经成功找到了 libdmlogmnr_client.so,但这个 .so 又依赖另一个动态库 libdmdpi.so,系统没找到这个依赖库。

解决方案:给当前运行用户临时指定环境变量。

[root@localhost LogmnrTest]# export LD_LIBRARY_PATH=/opt/dmdbms/bin:/opt/dmdbms/drivers/logmnr:$LD_LIBRARY_PATH^C
http://www.xdnf.cn/news/649567.html

相关文章:

  • Node.js全局对象详解:console、process与核心功能
  • 每日Prompt:黄沙大圣
  • 将网页带格式转化为PDF
  • python网络编程
  • AI时代新词-人工智能伦理审查(AI Ethics Review)
  • Set系列集合
  • 接口测试(详解)
  • Leetcode-4 数组异或操作
  • Java 调用 HTTP 和 HTTPS 的方式详解
  • 算法打卡第七天
  • maven模块化开发
  • 设计模式-简单工厂模式
  • leetcode 93. Restore IP Addresses
  • Spring boot基础
  • Flink 窗口与时间语义速记手册
  • day021-定时任务
  • macOS烧录stm32程序初步成功
  • 海思3519V200 上基于 Qt 的 OpenCV 和 MySql 配置开发
  • Qt MinGW编译出现obj file too big问题
  • #git 储藏库意外被清空 Error: bad index – Fatal: index file corrupt
  • centos7.9使用docker-compose安装kafka
  • 2025LitCTF--Crypto--WriteUp
  • MathQ-Verify:数学问题验证的五步流水线,为大模型推理筑牢数据基石
  • 【深度学习】6. 卷积神经网络,CNN反向传播,感受野,池化变种,局部连接机制,可视化实例
  • Kafka|基础入门
  • LLM outputs.loss 返回什么
  • 零基础设计模式——结构型模式 - 桥接模式
  • 如何做好一份网络安全技术文档?
  • 在SpringBoot项目中策略模式的使用
  • Spring 核心配置文件(spring.xml)构建指南