背景:
公司MES系统使用exp进行oracle数据库备份时,dmp文件80GB,导出耗时14Hour;(已经对数据库进行了各种精简步骤)
应该是个别table耗时比较长,所以想将导出耗时最多的table找出来;
而exp的log日志中并没有时间的记录,必须时刻盯着屏幕观察。也有考虑过用定时截图,后期看识别截图的方式,不过效果不太理想,最终使用了如下的方案。
需求说明:
使用C#程序执行exp操作,将执行的过程记录在log中,并附上时间,便于后续分析。
思路:
使用Proces调用cmd.exe,输入exp指令,将exp的StandardOutput重定向到cmd中以输出到log文件中
代码
1. using引用
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
2. 开始执行cmd指令
private Process process = null;
//开始执行的代码
private void btnStart_Click(object sender, EventArgs e)
{
if (!Directory.Exists(Path.GetDirectoryName(txtLogFilePath.Text)))
{
MessageBox.Show("log保存路径不可访问!",Text,MessageBoxButtons.OK ,MessageBoxIcon.Exclamation );
return;
}
process = new Process();
process.StartInfo.FileName = @"cmd.exe";
process.StartInfo.WorkingDirectory = ".";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
string strcmd;
strcmd = txtCMD.Text; //可先用ping www.baidu.com测试下效果
process.StandardInput.WriteLine(strcmd);
}
-->重要:若cmd命令为exp/imp时,必须加上Error信息相关的3行代码;此次走了很大的弯路,找了很久才发现的原因,希望能帮助大家!
process.StartInfo.RedirectStandardError = true;
process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
process.BeginErrorReadLine();
//因为跨进程必须要定义的委托
public delegate void Action<in T>(T obj);
//处理标准输出的事件
private void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
new Thread(() =>
{
Action<int> action = (data) =>
{
string strRow = DateTime.Now.ToString() + " " + outLine.Data;
AppendLog(strRow); //在此处记录到log文件,或输出到界面显示
if (outLine.Data.StartsWith("Export terminated successfully") ||
outLine.Data.StartsWith("导出成功终止"))
{
MessageBox.Show(outLine.Data);
}
};
try { Invoke(action, 0); } catch (Exception e) { }
}).Start();
}
}
//取消进程、向窗口发送ctrl+c指令
private void btnCancel_Click(object sender, EventArgs e)
{
if (process != null)
{
// 使用时
AttachConsole(process.Id); // 附加到目标进程的console
SetConsoleCtrlHandler(IntPtr.Zero, true); // 设置自己的ctrl+c处理,防止自己被终止
GenerateConsoleCtrlEvent(0, 0); // 发送ctrl+c(注意:这是向所有共享该console的进程发送)
FreeConsole(); // 脱离目标console
AppendLog("Canceled");
}
}
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool GenerateConsoleCtrlEvent(int dwCtrlEvent, int dwProcessGroupId);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(IntPtr handlerRoutine, bool add);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
static extern bool FreeConsole();
//其他通用方法
private void AppendLog(string message)
{
StringBuilder sb = new StringBuilder(this.rtbResult.Text);
this.rtbResult.Text = sb.AppendLine(message).ToString();
this.rtbResult.SelectionStart = this.rtbResult.Text.Length;
this.rtbResult.ScrollToCaret();
sb = null;
FileStream fs = null;
StreamWriter sw = null;
string logfile = txtLogFilePath.Text;
try
{
fs = new FileStream(logfile, FileMode.Append);
sw = new StreamWriter(fs, System.Text.Encoding.GetEncoding("utf-8"));
sw.WriteLine(message);
}
catch (Exception ex)
{
sw.WriteLine("发生异常:" + ex);
}
finally
{
sw.Close();
sw.Dispose();
fs.Close();
fs.Dispose();
}
}
private string CheckSlash(string strPath)
{
if (strPath.EndsWith("\\"))
{
return strPath;
}
else
{
return strPath + "\\";
}
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (process != null)
{
process.Close();
}
}